Reland of [Sync] Put session tracker in charge of maintaining local state.

Previously, the session tracker was only updated when local tabs were
available in the TabModel. It was not updated with sync data from a previous
session (and as a result, we had to pass around the restored_data list
at association time in order to handle restoring placeholder tabs).

The session tracker now gets updated at startup of previous local state,
which is then used when reassociating restored tabs. This is a purely
refactoring change that should not introduce user visible changes
(although internally the data is managed in a different way).

To make this happen, InitFromSyncModel now treats local tabs and remote
tabs the same. In addition, the SyncedSessionTracker now has a
ReassociateTab method that can be called to update the tab id of a
SessionTab. To make this work, the TabNodePool has been moved into
the SyncedSessionTracker, and in general data ownership has been
simplified (with quite a few new tests added).

This is all necessary in order to enable preserving tabs from a previous
session when they're not present in the TabModel (which can happen on
Android when a custom tab is opened, but the main tabbed activity is not
loaded).

BUG=639009

Review-Url: https://codereview.chromium.org/2499083004
Cr-Commit-Position: refs/heads/master@{#436786}
diff --git a/components/sync_sessions/sessions_sync_manager.cc b/components/sync_sessions/sessions_sync_manager.cc
index 04e01b7..9c55aa8 100644
--- a/components/sync_sessions/sessions_sync_manager.cc
+++ b/components/sync_sessions/sessions_sync_manager.cc
@@ -7,9 +7,12 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/format_macros.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "components/sync/base/hash_util.h"
 #include "components/sync/device_info/local_device_info_provider.h"
@@ -21,6 +24,7 @@
 #include "components/sync_sessions/synced_tab_delegate.h"
 #include "components/sync_sessions/synced_window_delegate.h"
 #include "components/sync_sessions/synced_window_delegates_getter.h"
+#include "components/sync_sessions/tab_node_pool.h"
 #include "components/variations/variations_associated_data.h"
 
 using sessions::SerializedNavigationEntry;
@@ -62,17 +66,31 @@
   return s1->modified_time > s2->modified_time;
 }
 
+std::string TabNodeIdToTag(const std::string& machine_tag, int tab_node_id) {
+  return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
+}
+
 std::string TagFromSpecifics(const sync_pb::SessionSpecifics& specifics) {
   if (specifics.has_header()) {
     return specifics.session_tag();
   } else if (specifics.has_tab()) {
-    return TabNodePool::TabIdToTag(specifics.session_tag(),
-                                   specifics.tab_node_id());
+    return TabNodeIdToTag(specifics.session_tag(), specifics.tab_node_id());
   } else {
     return std::string();
   }
 }
 
+sync_pb::SessionSpecifics SessionTabToSpecifics(
+    const sessions::SessionTab& session_tab,
+    const std::string& local_tag,
+    int tab_node_id) {
+  sync_pb::SessionSpecifics specifics;
+  specifics.mutable_tab()->CopyFrom(session_tab.ToSyncData());
+  specifics.set_session_tag(local_tag);
+  specifics.set_tab_node_id(tab_node_id);
+  return specifics;
+}
+
 }  // namespace
 
 // |local_device| is owned by ProfileSyncService, its lifetime exceeds
@@ -117,7 +135,6 @@
     std::unique_ptr<syncer::SyncErrorFactory> error_handler) {
   syncer::SyncMergeResult merge_result(type);
   DCHECK(session_tracker_.Empty());
-  DCHECK_EQ(0U, local_tab_pool_.Capacity());
 
   error_handler_ = std::move(error_handler);
   sync_processor_ = std::move(sync_processor);
@@ -153,13 +170,12 @@
     InitializeCurrentMachineTag(local_device_->GetLocalSyncCacheGUID());
   }
 
-  session_tracker_.SetLocalSessionTag(current_machine_tag_);
+  session_tracker_.SetLocalSessionTag(current_machine_tag());
 
   syncer::SyncChangeList new_changes;
 
   // First, we iterate over sync data to update our session_tracker_.
-  syncer::SyncDataList restored_tabs;
-  if (!InitFromSyncModel(initial_sync_data, &restored_tabs, &new_changes)) {
+  if (!InitFromSyncModel(initial_sync_data, &new_changes)) {
     // The sync db didn't have a header node for us. Create one.
     sync_pb::EntitySpecifics specifics;
     sync_pb::SessionSpecifics* base_specifics = specifics.mutable_session();
@@ -176,12 +192,12 @@
 #if defined(OS_ANDROID)
   std::string sync_machine_tag(
       BuildMachineTag(local_device_->GetLocalSyncCacheGUID()));
-  if (current_machine_tag_.compare(sync_machine_tag) != 0)
+  if (current_machine_tag().compare(sync_machine_tag) != 0)
     DeleteForeignSessionInternal(sync_machine_tag, &new_changes);
 #endif
 
   // Check if anything has changed on the local client side.
-  AssociateWindows(RELOAD_TABS, restored_tabs, &new_changes);
+  AssociateWindows(RELOAD_TABS, &new_changes);
   local_tab_pool_out_of_sync_ = false;
 
   merge_result.set_error(
@@ -193,7 +209,6 @@
 
 void SessionsSyncManager::AssociateWindows(
     ReloadTabsOption option,
-    const syncer::SyncDataList& restored_tabs,
     syncer::SyncChangeList* change_output) {
   const std::string local_tag = current_machine_tag();
   sync_pb::SessionSpecifics specifics;
@@ -262,6 +277,12 @@
         if (!synced_tab)
           continue;
 
+        // Placeholder tabs are those without WebContents, either because they
+        // were never loaded into memory or they were evicted from memory
+        // (typically only on Android devices). They only have a tab id, window
+        // id, and a saved synced id (corresponding to the tab node id). Note
+        // that only placeholders have this sync id, as it's necessary to
+        // properly reassociate the tab with the entity that was backing it.
         if (synced_tab->IsPlaceholderTab()) {
           // For tabs without WebContents update the |tab_id| and |window_id|,
           // as it could have changed after a session restore.
@@ -271,7 +292,7 @@
           if (synced_tab->GetSyncId() > TabNodePool::kInvalidTabNodeID &&
               tab_id > TabNodePool::kInvalidTabID) {
             AssociateRestoredPlaceholderTab(*synced_tab, tab_id, window_id,
-                                            restored_tabs, change_output);
+                                            change_output);
             found_tabs = true;
             window_s.add_tab(tab_id);
           }
@@ -304,8 +325,15 @@
       }
     }
   }
-  local_tab_pool_.DeleteUnassociatedTabNodes(change_output);
-  session_tracker_.CleanupSession(local_tag);
+  std::set<int> deleted_tab_node_ids;
+  session_tracker_.CleanupLocalTabs(&deleted_tab_node_ids);
+  for (int tab_node_id : deleted_tab_node_ids) {
+    std::string tab_node_tag =
+        TabNodeIdToTag(current_machine_tag(), tab_node_id);
+    change_output->push_back(syncer::SyncChange(
+        FROM_HERE, syncer::SyncChange::ACTION_DELETE,
+        syncer::SyncData::CreateLocalDelete(tab_node_tag, syncer::SESSIONS)));
+  }
 
   // Always update the header.  Sync takes care of dropping this update
   // if the entity specifics are identical (i.e windows, client name did
@@ -318,73 +346,62 @@
       syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
 }
 
-void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab,
+void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab_delegate,
                                        syncer::SyncChangeList* change_output) {
-  DCHECK(!tab->IsPlaceholderTab());
-  SessionID::id_type tab_id = tab->GetSessionId();
+  DCHECK(!tab_delegate->IsPlaceholderTab());
 
-  if (tab->IsBeingDestroyed()) {
-    // This tab is closing.
-    TabLinksMap::iterator tab_iter = local_tab_map_.find(tab_id);
-    if (tab_iter == local_tab_map_.end()) {
-      // We aren't tracking this tab (for example, sync setting page).
-      return;
-    }
-    local_tab_pool_.FreeTabNode(tab_iter->second->tab_node_id(), change_output);
-    local_tab_map_.erase(tab_iter);
+  if (tab_delegate->IsBeingDestroyed()) {
+    // Do nothing. By not proactively adding the tab to the session, it will be
+    // removed if necessary during subsequent cleanup.
     return;
   }
 
-  if (!tab->ShouldSync(sessions_client_))
+  if (!tab_delegate->ShouldSync(sessions_client_))
     return;
 
-  TabLinksMap::iterator local_tab_map_iter = local_tab_map_.find(tab_id);
-  TabLink* tab_link = nullptr;
+  SessionID::id_type tab_id = tab_delegate->GetSessionId();
+  DVLOG(1) << "Syncing tab " << tab_id << " from window "
+           << tab_delegate->GetWindowId();
 
-  if (local_tab_map_iter == local_tab_map_.end()) {
-    int tab_node_id = tab->GetSyncId();
-    // If there is an old sync node for the tab, reuse it.  If this is a new
-    // tab, get a sync node for it.
-    if (!local_tab_pool_.IsUnassociatedTabNode(tab_node_id)) {
-      tab_node_id = local_tab_pool_.GetFreeTabNode(change_output);
-      tab->SetSyncId(tab_node_id);
-    }
-    local_tab_pool_.AssociateTabNode(tab_node_id, tab_id);
-    tab_link = new TabLink(tab_node_id, tab);
-    local_tab_map_[tab_id] = make_linked_ptr<TabLink>(tab_link);
-  } else {
-    // This tab is already associated with a sync node, reuse it.
-    // Note: on some platforms the tab object may have changed, so we ensure
-    // the tab link is up to date.
-    tab_link = local_tab_map_iter->second.get();
-    local_tab_map_iter->second->set_tab(tab);
-  }
-  DCHECK(tab_link);
-  DCHECK_NE(tab_link->tab_node_id(), TabNodePool::kInvalidTabNodeID);
-  DVLOG(1) << "Reloading tab " << tab_id << " from window "
-           << tab->GetWindowId();
+  int tab_node_id = TabNodePool::kInvalidTabNodeID;
+  bool existing_tab_node =
+      session_tracker_.GetTabNodeForLocalTab(tab_id, &tab_node_id);
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
+  sessions::SessionTab* session_tab =
+      session_tracker_.GetTab(current_machine_tag(), tab_id);
 
-  // Write to sync model.
-  sync_pb::EntitySpecifics specifics;
-  LocalTabDelegateToSpecifics(*tab, specifics.mutable_session());
-  syncer::SyncData data = syncer::SyncData::CreateLocalData(
-      TabNodePool::TabIdToTag(current_machine_tag_, tab_link->tab_node_id()),
-      current_session_name_, specifics);
-  change_output->push_back(
-      syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
+  // Get the previously synced url.
+  int old_index = session_tab->normalized_navigation_index();
+  GURL old_url;
+  if (session_tab->navigations.size() > static_cast<size_t>(old_index))
+    old_url = session_tab->navigations[old_index].virtual_url();
 
-  int current_index = tab->GetCurrentEntryIndex();
-  const GURL new_url = tab->GetVirtualURLAtIndex(current_index);
-  if (new_url != tab_link->url()) {
-    tab_link->set_url(new_url);
-    favicon_cache_.OnFaviconVisited(new_url,
-                                    tab->GetFaviconURLAtIndex(current_index));
-    page_revisit_broadcaster_.OnPageVisit(
-        new_url, tab->GetTransitionAtIndex(current_index));
-  }
-
+  // Update the tracker's session representation.
+  SetSessionTabFromDelegate(*tab_delegate, base::Time::Now(), session_tab);
+  SetVariationIds(session_tab);
   session_tracker_.GetSession(current_machine_tag())->modified_time =
       base::Time::Now();
+
+  // Write to the sync model itself.
+  sync_pb::EntitySpecifics specifics;
+  specifics.mutable_session()->CopyFrom(
+      SessionTabToSpecifics(*session_tab, current_machine_tag(), tab_node_id));
+  syncer::SyncData data = syncer::SyncData::CreateLocalData(
+      TabNodeIdToTag(current_machine_tag(), tab_node_id), current_session_name_,
+      specifics);
+  change_output->push_back(syncer::SyncChange(
+      FROM_HERE, existing_tab_node ? syncer::SyncChange::ACTION_UPDATE
+                                   : syncer::SyncChange::ACTION_ADD,
+      data));
+
+  int current_index = tab_delegate->GetCurrentEntryIndex();
+  const GURL new_url = tab_delegate->GetVirtualURLAtIndex(current_index);
+  if (new_url != old_url) {
+    favicon_cache_.OnFaviconVisited(
+        new_url, tab_delegate->GetFaviconURLAtIndex(current_index));
+    page_revisit_broadcaster_.OnPageVisit(
+        new_url, tab_delegate->GetTransitionAtIndex(current_index));
+  }
 }
 
 bool SessionsSyncManager::RebuildAssociations() {
@@ -444,21 +461,14 @@
   // "interesting" by going to a valid URL, in which case it needs to be added
   // to the window's tab information. Similarly, if a tab became
   // "uninteresting", we remove it from the window's tab information.
-  AssociateWindows(DONT_RELOAD_TABS, syncer::SyncDataList(), &changes);
+  AssociateWindows(DONT_RELOAD_TABS, &changes);
   sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
 }
 
 void SessionsSyncManager::OnFaviconsChanged(const std::set<GURL>& page_urls,
                                             const GURL& /* icon_url */) {
-  // TODO(zea): consider a separate container for tabs with outstanding favicon
-  // loads so we don't have to iterate through all tabs comparing urls.
-  for (const GURL& page_url : page_urls) {
-    for (TabLinksMap::iterator tab_iter = local_tab_map_.begin();
-         tab_iter != local_tab_map_.end(); ++tab_iter) {
-      if (tab_iter->second->url() == page_url)
-        favicon_cache_.OnPageFaviconUpdated(page_url);
-    }
-  }
+  for (const GURL& page_url : page_urls)
+    favicon_cache_.OnPageFaviconUpdated(page_url);
 }
 
 void SessionsSyncManager::StopSyncing(syncer::ModelType type) {
@@ -471,8 +481,6 @@
   sync_processor_.reset(nullptr);
   error_handler_.reset();
   session_tracker_.Clear();
-  local_tab_map_.clear();
-  local_tab_pool_.Clear();
   current_machine_tag_.clear();
   current_session_name_.clear();
   local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID;
@@ -495,22 +503,17 @@
       current_machine_tag(), current_session_name_, header_entity);
   list.push_back(data);
 
-  for (auto win_iter = session->windows.begin();
-       win_iter != session->windows.end(); ++win_iter) {
-    for (auto tabs_iter = win_iter->second->tabs.begin();
-         tabs_iter != win_iter->second->tabs.end(); ++tabs_iter) {
+  for (auto& win_iter : session->windows) {
+    for (auto& tab : win_iter.second->tabs) {
+      // TODO(zea): replace with with the correct tab node id once there's a
+      // sync specific wrapper for SessionTab. This method is only used in
+      // tests though, so it's fine for now. crbug.com/662597
+      int tab_node_id = 0;
       sync_pb::EntitySpecifics entity;
-      sync_pb::SessionSpecifics* specifics = entity.mutable_session();
-      specifics->mutable_tab()->MergeFrom((*tabs_iter)->ToSyncData());
-      specifics->set_session_tag(current_machine_tag_);
-
-      TabLinksMap::const_iterator tab_map_iter =
-          local_tab_map_.find((*tabs_iter)->tab_id.id());
-      DCHECK(tab_map_iter != local_tab_map_.end());
-      specifics->set_tab_node_id(tab_map_iter->second->tab_node_id());
+      entity.mutable_session()->CopyFrom(
+          SessionTabToSpecifics(*tab, current_machine_tag(), tab_node_id));
       syncer::SyncData data = syncer::SyncData::CreateLocalData(
-          TabNodePool::TabIdToTag(current_machine_tag_,
-                                  specifics->tab_node_id()),
+          TabNodeIdToTag(current_machine_tag(), tab_node_id),
           current_session_name_, entity);
       list.push_back(data);
     }
@@ -519,7 +522,7 @@
 }
 
 bool SessionsSyncManager::GetLocalSession(const SyncedSession** local_session) {
-  if (current_machine_tag_.empty())
+  if (current_machine_tag().empty())
     return false;
   *local_session = session_tracker_.GetSession(current_machine_tag());
   return true;
@@ -579,7 +582,7 @@
           LOG(WARNING) << "Dropping modification to local session.";
           return syncer::SyncError();
         }
-        UpdateTrackerWithForeignSession(
+        UpdateTrackerWithSpecifics(
             session, syncer::SyncDataRemote(it->sync_data()).GetModifiedTime());
         break;
       default:
@@ -601,7 +604,7 @@
     return syncer::SyncChange(
         FROM_HERE, SyncChange::ACTION_DELETE,
         SyncData::CreateLocalDelete(
-            TabNodePool::TabIdToTag(current_machine_tag(), tab.tab_node_id()),
+            TabNodeIdToTag(current_machine_tag(), tab.tab_node_id()),
             syncer::SESSIONS));
   }
 }
@@ -617,7 +620,6 @@
 
 bool SessionsSyncManager::InitFromSyncModel(
     const syncer::SyncDataList& sync_data,
-    syncer::SyncDataList* restored_tabs,
     syncer::SyncChangeList* new_changes) {
   bool found_current_header = false;
   int bad_foreign_hash_count = 0;
@@ -635,7 +637,7 @@
         new_changes->push_back(tombstone);
     } else if (specifics.session_tag() != current_machine_tag()) {
       if (TagHashFromSpecifics(specifics) == remote.GetClientTagHash()) {
-        UpdateTrackerWithForeignSession(specifics, remote.GetModifiedTime());
+        UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime());
       } else {
         // In the past, like years ago, we believe that some session data was
         // created with bad tag hashes. This causes any change this client makes
@@ -654,6 +656,10 @@
         found_current_header = true;
         if (specifics.header().has_client_name())
           current_session_name_ = specifics.header().client_name();
+
+        // TODO(zea): crbug.com/639009 update the tracker with the specifics
+        // from the header node as well. This will be necessary to preserve
+        // the set of open tabs when a custom tab is opened.
       } else {
         if (specifics.has_header() || !specifics.has_tab()) {
           LOG(WARNING) << "Found more than one session header node with local "
@@ -662,10 +668,13 @@
           if (tombstone.IsValid())
             new_changes->push_back(tombstone);
         } else {
-          // This is a valid old tab node, add it to the pool so it can be
-          // reused for reassociation.
-          local_tab_pool_.AddTabNode(specifics.tab_node_id());
-          restored_tabs->push_back(*it);
+          // This is a valid old tab node, add it to the tracker and associate
+          // it.
+          DVLOG(1) << "Associating local tab " << specifics.tab().tab_id()
+                   << " with node " << specifics.tab_node_id();
+          session_tracker_.ReassociateLocalTab(specifics.tab_node_id(),
+                                               specifics.tab().tab_id());
+          UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime());
         }
       }
     }
@@ -677,7 +686,7 @@
   session_tracker_.LookupAllForeignSessions(&sessions,
                                             SyncedSessionTracker::RAW);
   for (const auto* session : sessions) {
-    session_tracker_.CleanupSession(session->session_tag);
+    session_tracker_.CleanupForeignSession(session->session_tag);
   }
 
   UMA_HISTOGRAM_COUNTS_100("Sync.SessionsBadForeignHashOnMergeCount",
@@ -686,70 +695,83 @@
   return found_current_header;
 }
 
-void SessionsSyncManager::UpdateTrackerWithForeignSession(
+void SessionsSyncManager::UpdateTrackerWithSpecifics(
     const sync_pb::SessionSpecifics& specifics,
     const base::Time& modification_time) {
-  std::string foreign_session_tag = specifics.session_tag();
-  DCHECK_NE(foreign_session_tag, current_machine_tag());
-
-  SyncedSession* foreign_session =
-      session_tracker_.GetSession(foreign_session_tag);
+  std::string session_tag = specifics.session_tag();
+  SyncedSession* session = session_tracker_.GetSession(session_tag);
   if (specifics.has_header()) {
-    // Read in the header data for this foreign session. Header data is
+    // Read in the header data for this session. Header data is
     // essentially a collection of windows, each of which has an ordered id list
     // for their tabs.
 
     if (!IsValidSessionHeader(specifics.header())) {
-      LOG(WARNING) << "Ignoring foreign session node with invalid header "
-                   << "and tag " << foreign_session_tag << ".";
+      LOG(WARNING) << "Ignoring session node with invalid header "
+                   << "and tag " << session_tag << ".";
       return;
     }
 
     // Load (or create) the SyncedSession object for this client.
     const sync_pb::SessionHeader& header = specifics.header();
-    PopulateSessionHeaderFromSpecifics(header, modification_time,
-                                       foreign_session);
+    PopulateSessionHeaderFromSpecifics(header, modification_time, session);
 
     // Reset the tab/window tracking for this session (must do this before
     // we start calling PutWindowInSession and PutTabInWindow so that all
     // unused tabs/windows get cleared by the CleanupSession(...) call).
-    session_tracker_.ResetSessionTracking(foreign_session_tag);
+    session_tracker_.ResetSessionTracking(session_tag);
 
     // Process all the windows and their tab information.
     int num_windows = header.window_size();
-    DVLOG(1) << "Associating " << foreign_session_tag << " with " << num_windows
+    DVLOG(1) << "Populating " << session_tag << " with " << num_windows
              << " windows.";
 
     for (int i = 0; i < num_windows; ++i) {
       const sync_pb::SessionWindow& window_s = header.window(i);
       SessionID::id_type window_id = window_s.window_id();
-      session_tracker_.PutWindowInSession(foreign_session_tag, window_id);
-      BuildSyncedSessionFromSpecifics(
-          foreign_session_tag, window_s, modification_time,
-          foreign_session->windows[window_id].get());
+      session_tracker_.PutWindowInSession(session_tag, window_id);
+      BuildSyncedSessionFromSpecifics(session_tag, window_s, modification_time,
+                                      session->windows[window_id].get());
     }
     // Delete any closed windows and unused tabs as necessary.
-    session_tracker_.CleanupSession(foreign_session_tag);
+    session_tracker_.CleanupForeignSession(session_tag);
   } else if (specifics.has_tab()) {
     const sync_pb::SessionTab& tab_s = specifics.tab();
     SessionID::id_type tab_id = tab_s.tab_id();
+    DVLOG(1) << "Populating " << session_tag << "'s tab id " << tab_id
+             << " from node " << specifics.tab_node_id();
 
-    const sessions::SessionTab* existing_tab;
-    if (session_tracker_.LookupSessionTab(foreign_session_tag, tab_id,
-                                          &existing_tab) &&
-        existing_tab->timestamp > modification_time) {
-      // Force the tracker to remember this tab node id, even if it isn't
-      // currently being used.
-      session_tracker_.GetTab(foreign_session_tag, tab_id,
-                              specifics.tab_node_id());
-      DVLOG(1) << "Ignoring " << foreign_session_tag << "'s session tab "
-               << tab_id << " with earlier modification time";
+    // Ensure the tracker is aware of the tab node id. Deleting foreign sessions
+    // requires deleting all relevant tab nodes, and it's easier to track the
+    // tab node ids themselves separately from the tab ids.
+    //
+    // Note that TabIDs are not stable across restarts of a client. Consider
+    // this example with two tabs:
+    //
+    // http://a.com  TabID1 --> NodeIDA
+    // http://b.com  TabID2 --> NodeIDB
+    //
+    // After restart, tab ids are reallocated. e.g, one possibility:
+    // http://a.com TabID2 --> NodeIDA
+    // http://b.com TabID1 --> NodeIDB
+    //
+    // If that happened on a remote client, here we will see an update to
+    // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
+    // with tab_node_id changing from NodeIDB to NodeIDA.
+    //
+    // We can also wind up here if we created this tab as an out-of-order
+    // update to the header node for this session before actually associating
+    // the tab itself, so the tab node id wasn't available at the time and
+    // is currently kInvalidTabNodeID.
+    //
+    // In both cases, we can safely throw it into the set of node ids.
+    session_tracker_.OnTabNodeSeen(session_tag, specifics.tab_node_id());
+    sessions::SessionTab* tab = session_tracker_.GetTab(session_tag, tab_id);
+    if (tab->timestamp > modification_time) {
+      DVLOG(1) << "Ignoring " << session_tag << "'s session tab " << tab_id
+               << " with earlier modification time";
       return;
     }
 
-    sessions::SessionTab* tab = session_tracker_.GetTab(
-        foreign_session_tag, tab_id, specifics.tab_node_id());
-
     // Update SessionTab based on protobuf.
     tab->SetFromSyncData(tab_s, modification_time);
 
@@ -758,11 +780,11 @@
     RefreshFaviconVisitTimesFromForeignTab(tab_s, modification_time);
 
     // Update the last modified time.
-    if (foreign_session->modified_time < modification_time)
-      foreign_session->modified_time = modification_time;
+    if (session->modified_time < modification_time)
+      session->modified_time = modification_time;
   } else {
-    LOG(WARNING) << "Ignoring foreign session node with missing header/tab "
-                 << "fields and tag " << foreign_session_tag << ".";
+    LOG(WARNING) << "Ignoring session node with missing header/tab "
+                 << "fields and tag " << session_tag << ".";
   }
 }
 
@@ -780,8 +802,6 @@
     DVLOG(1) << "Creating session sync guid: " << current_machine_tag_;
     sync_prefs_->SetSyncSessionsGUID(current_machine_tag_);
   }
-
-  local_tab_pool_.SetMachineTag(current_machine_tag_);
 }
 
 // static
@@ -891,7 +911,7 @@
   }
 
   std::set<int> tab_node_ids_to_delete;
-  session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete);
+  session_tracker_.LookupForeignTabNodeIds(tag, &tab_node_ids_to_delete);
   if (DisassociateForeignSession(tag)) {
     // Only tell sync to delete the header if there was one.
     change_output->push_back(
@@ -900,10 +920,10 @@
   }
   for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin();
        it != tab_node_ids_to_delete.end(); ++it) {
-    change_output->push_back(syncer::SyncChange(
-        FROM_HERE, SyncChange::ACTION_DELETE,
-        SyncData::CreateLocalDelete(TabNodePool::TabIdToTag(tag, *it),
-                                    syncer::SESSIONS)));
+    change_output->push_back(
+        syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE,
+                           SyncData::CreateLocalDelete(TabNodeIdToTag(tag, *it),
+                                                       syncer::SESSIONS)));
   }
   if (!sessions_updated_callback_.is_null())
     sessions_updated_callback_.Run();
@@ -913,7 +933,7 @@
     const std::string& foreign_session_tag) {
   DCHECK_NE(foreign_session_tag, current_machine_tag());
   DVLOG(1) << "Disassociating session " << foreign_session_tag;
-  return session_tracker_.DeleteSession(foreign_session_tag);
+  return session_tracker_.DeleteForeignSession(foreign_session_tag);
 }
 
 bool SessionsSyncManager::GetForeignSession(
@@ -959,66 +979,32 @@
   return success;
 }
 
-void SessionsSyncManager::LocalTabDelegateToSpecifics(
-    const SyncedTabDelegate& tab_delegate,
-    sync_pb::SessionSpecifics* specifics) {
-  sessions::SessionTab* session_tab = nullptr;
-  session_tab = session_tracker_.GetTab(current_machine_tag(),
-                                        tab_delegate.GetSessionId(),
-                                        tab_delegate.GetSyncId());
-  SetSessionTabFromDelegate(tab_delegate, base::Time::Now(), session_tab);
-  SetVariationIds(session_tab);
-  sync_pb::SessionTab tab_s = session_tab->ToSyncData();
-  specifics->set_session_tag(current_machine_tag_);
-  specifics->set_tab_node_id(tab_delegate.GetSyncId());
-  specifics->mutable_tab()->CopyFrom(tab_s);
-}
-
 void SessionsSyncManager::AssociateRestoredPlaceholderTab(
     const SyncedTabDelegate& tab_delegate,
     SessionID::id_type new_tab_id,
     SessionID::id_type new_window_id,
-    const syncer::SyncDataList& restored_tabs,
     syncer::SyncChangeList* change_output) {
   DCHECK_NE(tab_delegate.GetSyncId(), TabNodePool::kInvalidTabNodeID);
-  // Rewrite the tab using |restored_tabs| to retrieve the specifics.
-  if (restored_tabs.empty()) {
-    DLOG(WARNING) << "Can't Update tab ID.";
-    return;
-  }
 
-  for (syncer::SyncDataList::const_iterator it = restored_tabs.begin();
-       it != restored_tabs.end(); ++it) {
-    if (it->GetSpecifics().session().tab_node_id() !=
-        tab_delegate.GetSyncId()) {
-      continue;
-    }
+  // Update tracker with the new association (and inform it of the tab node
+  // in the process).
+  session_tracker_.ReassociateLocalTab(tab_delegate.GetSyncId(), new_tab_id);
 
-    sync_pb::EntitySpecifics entity;
-    sync_pb::SessionSpecifics* specifics = entity.mutable_session();
-    specifics->CopyFrom(it->GetSpecifics().session());
-    DCHECK(specifics->has_tab());
+  // Update the window id on the SessionTab itself.
+  sessions::SessionTab* local_tab =
+      session_tracker_.GetTab(current_machine_tag(), new_tab_id);
+  local_tab->window_id.set_id(new_window_id);
 
-    // Update tab node pool with the new association.
-    local_tab_pool_.ReassociateTabNode(tab_delegate.GetSyncId(), new_tab_id);
-    TabLink* tab_link = new TabLink(tab_delegate.GetSyncId(), &tab_delegate);
-    local_tab_map_[new_tab_id] = make_linked_ptr<TabLink>(tab_link);
-
-    if (specifics->tab().tab_id() == new_tab_id &&
-        specifics->tab().window_id() == new_window_id)
-      return;
-
-    // Either the tab_id or window_id changed (e.g due to session restore), so
-    // update the sync node.
-    specifics->mutable_tab()->set_tab_id(new_tab_id);
-    specifics->mutable_tab()->set_window_id(new_window_id);
-    syncer::SyncData data = syncer::SyncData::CreateLocalData(
-        TabNodePool::TabIdToTag(current_machine_tag_, specifics->tab_node_id()),
-        current_session_name_, entity);
-    change_output->push_back(
-        syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
-    return;
-  }
+  // Rewrite the specifics based on the reassociated SessionTab to preserve
+  // the new tab and window ids.
+  sync_pb::EntitySpecifics entity;
+  entity.mutable_session()->CopyFrom(SessionTabToSpecifics(
+      *local_tab, current_machine_tag(), tab_delegate.GetSyncId()));
+  syncer::SyncData data = syncer::SyncData::CreateLocalData(
+      TabNodeIdToTag(current_machine_tag(), tab_delegate.GetSyncId()),
+      current_session_name_, entity);
+  change_output->push_back(
+      syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
 }
 
 // static
diff --git a/components/sync_sessions/sessions_sync_manager.h b/components/sync_sessions/sessions_sync_manager.h
index 5f2b5981..1f6af39 100644
--- a/components/sync_sessions/sessions_sync_manager.h
+++ b/components/sync_sessions/sessions_sync_manager.h
@@ -30,7 +30,6 @@
 #include "components/sync_sessions/revisit/page_revisit_broadcaster.h"
 #include "components/sync_sessions/synced_session.h"
 #include "components/sync_sessions/synced_session_tracker.h"
-#include "components/sync_sessions/tab_node_pool.h"
 
 namespace syncer {
 class LocalDeviceInfoProvider;
@@ -118,37 +117,6 @@
   void DoGarbageCollection();
 
  private:
-  // Keep all the links to local tab data in one place. A tab_node_id and tab
-  // must be passed at creation. The tab_node_id is not mutable, although
-  // all other fields are.
-  class TabLink {
-   public:
-    TabLink(int tab_node_id, const SyncedTabDelegate* tab)
-        : tab_node_id_(tab_node_id), tab_(tab) {}
-
-    void set_tab(const SyncedTabDelegate* tab) { tab_ = tab; }
-    void set_url(const GURL& url) { url_ = url; }
-
-    int tab_node_id() const { return tab_node_id_; }
-    const SyncedTabDelegate* tab() const { return tab_; }
-    const GURL& url() const { return url_; }
-
-   private:
-    // The id for the sync node this tab is stored in.
-    const int tab_node_id_;
-
-    // The tab object itself.
-    const SyncedTabDelegate* tab_;
-
-    // The currently visible url of the tab (used for syncing favicons).
-    GURL url_;
-
-    DISALLOW_COPY_AND_ASSIGN(TabLink);
-  };
-
-  // Container for accessing local tab data by tab id.
-  typedef std::map<SessionID::id_type, linked_ptr<TabLink>> TabLinksMap;
-
   friend class extensions::ExtensionSessionsTest;
   friend class SessionsSyncManagerTest;
   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, PopulateSessionHeader);
@@ -182,20 +150,16 @@
 
   void InitializeCurrentMachineTag(const std::string& cache_guid);
 
-  // Load and add window or tab data for a foreign session to our internal
+  // Load and add window or tab data from synced specifics to our internal
   // tracking.
-  void UpdateTrackerWithForeignSession(
-      const sync_pb::SessionSpecifics& specifics,
-      const base::Time& modification_time);
+  void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
+                                  const base::Time& modification_time);
 
   // Returns true if |sync_data| contained a header node for the current
-  // machine, false otherwise. |restored_tabs| is a filtered tab-only
-  // subset of |sync_data| returned by this function for convenience.
-  // |new_changes| is a link to the SyncChange pipeline that exists in the
-  // caller's context. This function will append necessary changes for
-  // processing later.
+  // machine, false otherwise. |new_changes| is a link to the SyncChange
+  // pipeline that exists in the caller's context. This function will append
+  // necessary changes for processing later.
   bool InitFromSyncModel(const syncer::SyncDataList& sync_data,
-                         syncer::SyncDataList* restored_tabs,
                          syncer::SyncChangeList* new_changes);
 
   // Helper to construct a deletion SyncChange for a *tab node*.
@@ -244,11 +208,6 @@
   // RELOAD_TABS will additionally cause a resync of all tabs (same as calling
   // AssociateTabs with a vector of all tabs).
   //
-  // |restored_tabs| is a filtered tab-only subset of initial sync data, if
-  // available (during MergeDataAndStartSyncing). It can be used to obtain
-  // baseline SessionSpecifics for tabs we can't fully associate any other
-  // way because they don't yet have a WebContents.
-  //
   // Returns: false if the local session's sync nodes were deleted and
   // reassociation is necessary, true otherwise.
   //
@@ -257,7 +216,6 @@
   // changes for processing later.
   enum ReloadTabsOption { RELOAD_TABS, DONT_RELOAD_TABS };
   void AssociateWindows(ReloadTabsOption option,
-                        const syncer::SyncDataList& restored_tabs,
                         syncer::SyncChangeList* change_output);
 
   // Loads and reassociates the local tabs referenced in |tabs|.
@@ -286,17 +244,10 @@
   // as they may have changed after a session was restored.  This method
   // compares new_tab_id and new_window_id against the previously persisted tab
   // ID and window ID (from our TabNodePool) and updates them if either differs.
-  // |restored_tabs| is a filtered tab-only subset of initial sync data, if
-  // available (during MergeDataAndStartSyncing). It can be used to obtain
-  // baseline SessionSpecifics for tabs we can't fully associate any other
-  // way because they don't yet have a WebContents.
-  // TODO(tim): Bug 98892. We should be able to test this for this on android
-  // even though we didn't have tests for old API-based sessions sync.
   void AssociateRestoredPlaceholderTab(
       const SyncedTabDelegate& tab_delegate,
       SessionID::id_type new_tab_id,
       SessionID::id_type new_window_id,
-      const syncer::SyncDataList& restored_tabs,
       syncer::SyncChangeList* change_output);
 
   // Stops and re-starts syncing to rebuild association mappings. Returns true
@@ -323,15 +274,9 @@
   // The client of this sync sessions datatype.
   SyncSessionsClient* const sessions_client_;
 
-  // Mapping of current open (local) tabs to their sync identifiers.
-  TabLinksMap local_tab_map_;
-
   SyncedSessionTracker session_tracker_;
   FaviconCache favicon_cache_;
 
-  // Pool of used/available sync nodes associated with local tabs.
-  TabNodePool local_tab_pool_;
-
   // Tracks whether our local representation of which sync nodes map to what
   // tabs (belonging to the current local session) is inconsistent.  This can
   // happen if a foreign client deems our session as "stale" and decides to
diff --git a/components/sync_sessions/synced_session_tracker.cc b/components/sync_sessions/synced_session_tracker.cc
index a74b877c..e797295b 100644
--- a/components/sync_sessions/synced_session_tracker.cc
+++ b/components/sync_sessions/synced_session_tracker.cc
@@ -53,6 +53,8 @@
 
 void SyncedSessionTracker::SetLocalSessionTag(
     const std::string& local_session_tag) {
+  DCHECK(local_session_tag_.empty());
+  DCHECK(!local_session_tag.empty());
   local_session_tag_ = local_session_tag;
 }
 
@@ -107,8 +109,9 @@
   return true;
 }
 
-void SyncedSessionTracker::LookupTabNodeIds(const std::string& session_tag,
-                                            std::set<int>* tab_node_ids) {
+void SyncedSessionTracker::LookupForeignTabNodeIds(
+    const std::string& session_tag,
+    std::set<int>* tab_node_ids) const {
   tab_node_ids->clear();
   auto session_iter = synced_session_map_.find(session_tag);
   if (session_iter != synced_session_map_.end()) {
@@ -144,7 +147,9 @@
   return synced_session_map_[session_tag].get();
 }
 
-bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
+bool SyncedSessionTracker::DeleteForeignSession(
+    const std::string& session_tag) {
+  DCHECK_NE(local_session_tag_, session_tag);
   unmapped_windows_.erase(session_tag);
   unmapped_tabs_.erase(session_tag);
 
@@ -188,13 +193,14 @@
 
 void SyncedSessionTracker::DeleteForeignTab(const std::string& session_tag,
                                             int tab_node_id) {
+  DCHECK_NE(local_session_tag_, session_tag);
   auto session_iter = synced_session_map_.find(session_tag);
   if (session_iter != synced_session_map_.end()) {
     session_iter->second->tab_node_ids.erase(tab_node_id);
   }
 }
 
-void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
+void SyncedSessionTracker::CleanupSessionImpl(const std::string& session_tag) {
   for (const auto& window_pair : unmapped_windows_[session_tag])
     synced_window_map_[session_tag].erase(window_pair.first);
   unmapped_windows_[session_tag].clear();
@@ -236,7 +242,7 @@
                                           SessionID::id_type tab_id,
                                           size_t tab_index) {
   // We're called here for two reasons. 1) We've received an update to the
-  // SessionWindow information of a SessionHeader node for a foreign session,
+  // SessionWindow information of a SessionHeader node for a session,
   // and 2) The SessionHeader node for our local session changed. In both cases
   // we need to update our tracking state to reflect the change.
   //
@@ -246,7 +252,7 @@
   // We know that we will eventually process (via GetTab) every single tab node
   // in the system, so we permit ourselves to use kInvalidTabNodeID here and
   // rely on the later update to build the mapping (or a restart).
-  GetTabImpl(session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
+  GetTab(session_tag, tab_id);
 
   // The tab should be unmapped.
   std::unique_ptr<sessions::SessionTab> tab;
@@ -269,46 +275,18 @@
   window_tabs[tab_index] = std::move(tab);
 }
 
-sessions::SessionTab* SyncedSessionTracker::GetTab(
-    const std::string& session_tag,
-    SessionID::id_type tab_id,
-    int tab_node_id) {
-  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
-  return GetTabImpl(session_tag, tab_id, tab_node_id);
+void SyncedSessionTracker::OnTabNodeSeen(const std::string& session_tag,
+                                         int tab_node_id) {
+  GetSession(session_tag)->tab_node_ids.insert(tab_node_id);
 }
 
-sessions::SessionTab* SyncedSessionTracker::GetTabImpl(
+sessions::SessionTab* SyncedSessionTracker::GetTab(
     const std::string& session_tag,
-    SessionID::id_type tab_id,
-    int tab_node_id) {
+    SessionID::id_type tab_id) {
   sessions::SessionTab* tab_ptr = nullptr;
   auto iter = synced_tab_map_[session_tag].find(tab_id);
   if (iter != synced_tab_map_[session_tag].end()) {
     tab_ptr = iter->second;
-    if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
-        tab_id != TabNodePool::kInvalidTabID) {
-      // TabIDs are not stable across restarts of a client. Consider this
-      // example with two tabs:
-      //
-      // http://a.com  TabID1 --> NodeIDA
-      // http://b.com  TabID2 --> NodeIDB
-      //
-      // After restart, tab ids are reallocated. e.g, one possibility:
-      // http://a.com TabID2 --> NodeIDA
-      // http://b.com TabID1 --> NodeIDB
-      //
-      // If that happend on a remote client, here we will see an update to
-      // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
-      // with tab_node_id changing from NodeIDB to NodeIDA.
-      //
-      // We can also wind up here if we created this tab as an out-of-order
-      // update to the header node for this session before actually associating
-      // the tab itself, so the tab node id wasn't available at the time and
-      // is currently kInvalidTabNodeID.
-      //
-      // In both cases, we can safely throw it into the set of node ids.
-      GetSession(session_tag)->tab_node_ids.insert(tab_node_id);
-    }
 
     if (VLOG_IS_ON(1)) {
       std::string title;
@@ -328,7 +306,6 @@
     tab->tab_id.set_id(tab_id);
     synced_tab_map_[session_tag][tab_id] = tab_ptr;
     unmapped_tabs_[session_tag][tab_id] = std::move(tab);
-    GetSession(session_tag)->tab_node_ids.insert(tab_node_id);
     DVLOG(1) << "Getting "
              << (session_tag == local_session_tag_ ? "local session"
                                                    : session_tag)
@@ -339,6 +316,90 @@
   return tab_ptr;
 }
 
+void SyncedSessionTracker::CleanupForeignSession(
+    const std::string& session_tag) {
+  DCHECK_NE(local_session_tag_, session_tag);
+  CleanupSessionImpl(session_tag);
+}
+
+void SyncedSessionTracker::CleanupLocalTabs(std::set<int>* deleted_node_ids) {
+  DCHECK(!local_session_tag_.empty());
+  for (const auto& tab_pair : unmapped_tabs_[local_session_tag_])
+    local_tab_pool_.FreeTab(tab_pair.first);
+  CleanupSessionImpl(local_session_tag_);
+  local_tab_pool_.CleanupTabNodes(deleted_node_ids);
+  for (int tab_node_id : *deleted_node_ids) {
+    GetSession(local_session_tag_)->tab_node_ids.erase(tab_node_id);
+  }
+}
+
+bool SyncedSessionTracker::GetTabNodeForLocalTab(int tab_id, int* tab_node_id) {
+  DCHECK(!local_session_tag_.empty());
+  // Ensure a placeholder SessionTab is in place, if not already.
+  // Although we don't need a SessionTab to fulfill this request, this forces
+  // the
+  // creation of one if it doesn't already exist. This helps to make sure we're
+  // tracking this |tab_id| if |local_tab_pool_| is, and everyone's data
+  // structures
+  // are kept in sync and as consistent as possible.
+  GetTab(local_session_tag_, tab_id);  // Ignore result.
+
+  bool reused_existing_tab =
+      local_tab_pool_.GetTabNodeForTab(tab_id, tab_node_id);
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, *tab_node_id);
+  GetSession(local_session_tag_)->tab_node_ids.insert(*tab_node_id);
+  return reused_existing_tab;
+}
+
+void SyncedSessionTracker::ReassociateLocalTab(int tab_node_id,
+                                               SessionID::id_type new_tab_id) {
+  DCHECK(!local_session_tag_.empty());
+  DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
+  DCHECK_NE(TabNodePool::kInvalidTabID, new_tab_id);
+
+  SessionID::id_type old_tab_id =
+      local_tab_pool_.GetTabIdFromTabNodeId(tab_node_id);
+  local_tab_pool_.ReassociateTabNode(tab_node_id, new_tab_id);
+
+  sessions::SessionTab* tab_ptr = nullptr;
+
+  auto old_tab_iter = synced_tab_map_[local_session_tag_].find(old_tab_id);
+  if (old_tab_iter != synced_tab_map_[local_session_tag_].end()) {
+    tab_ptr = old_tab_iter->second;
+    // Remove the tab from the synced tab map under the old id.
+    synced_tab_map_[local_session_tag_].erase(old_tab_iter);
+  } else {
+    // It's possible a placeholder is already in place for the new tab. If so,
+    // reuse it, otherwise create a new one (which will default to unmapped).
+    tab_ptr = GetTab(local_session_tag_, new_tab_id);
+  }
+
+  // If the old tab is unmapped, update the tab id under which it is indexed.
+  auto unmapped_tabs_iter = unmapped_tabs_[local_session_tag_].find(old_tab_id);
+  if (old_tab_id != TabNodePool::kInvalidTabID &&
+      unmapped_tabs_iter != unmapped_tabs_[local_session_tag_].end()) {
+    std::unique_ptr<sessions::SessionTab> tab =
+        std::move(unmapped_tabs_iter->second);
+    DCHECK_EQ(tab_ptr, tab.get());
+    unmapped_tabs_[local_session_tag_].erase(unmapped_tabs_iter);
+    unmapped_tabs_[local_session_tag_][new_tab_id] = std::move(tab);
+  }
+
+  // Update the tab id.
+  if (old_tab_id != TabNodePool::kInvalidTabID) {
+    DVLOG(1) << "Remapped tab " << old_tab_id << " with node " << tab_node_id
+             << " to tab " << new_tab_id;
+  } else {
+    DVLOG(1) << "Mapped new tab node " << tab_node_id << " to tab "
+             << new_tab_id;
+  }
+  tab_ptr->tab_id.set_id(new_tab_id);
+
+  // Add the tab back into the tab map with the new id.
+  synced_tab_map_[local_session_tag_][new_tab_id] = tab_ptr;
+  GetSession(local_session_tag_)->tab_node_ids.insert(tab_node_id);
+}
+
 void SyncedSessionTracker::Clear() {
   // Cleanup unmapped tabs and windows.
   unmapped_windows_.clear();
@@ -352,6 +413,7 @@
   synced_window_map_.clear();
   synced_tab_map_.clear();
 
+  local_tab_pool_.Clear();
   local_session_tag_.clear();
 }
 
diff --git a/components/sync_sessions/synced_session_tracker.h b/components/sync_sessions/synced_session_tracker.h
index 35123d7..e27ba21 100644
--- a/components/sync_sessions/synced_session_tracker.h
+++ b/components/sync_sessions/synced_session_tracker.h
@@ -40,8 +40,7 @@
   explicit SyncedSessionTracker(SyncSessionsClient* sessions_client);
   ~SyncedSessionTracker();
 
-  // We track and distinguish the local session from foreign sessions.
-  void SetLocalSessionTag(const std::string& local_session_tag);
+  // **** Synced session/tab query methods. ****
 
   // Fill a preallocated vector with all foreign sessions we're tracking (skips
   // the local session object). SyncedSession ownership remains within the
@@ -51,6 +50,11 @@
   bool LookupAllForeignSessions(std::vector<const SyncedSession*>* sessions,
                                 SessionLookup lookup) const;
 
+  // Fills |tab_node_ids| with the tab node ids (see GetTab) for all the tabs*
+  // associated with the session having tag |session_tag|.
+  void LookupForeignTabNodeIds(const std::string& session_tag,
+                               std::set<int>* tab_node_ids) const;
+
   // Attempts to look up the session windows associatd with the session given
   // by |session_tag|. Ownership of SessionWindows stays within the
   // SyncedSessionTracker.
@@ -76,15 +80,13 @@
   // this won't create-if-not-present.
   bool LookupLocalSession(const SyncedSession** output) const;
 
+  // **** Methods for manipulating synced sessions and tabs. ****
+
   // Returns a pointer to the SyncedSession object associated with
   // |session_tag|. If none exists, creates one. Ownership of the
   // SyncedSession remains within the SyncedSessionTracker.
   SyncedSession* GetSession(const std::string& session_tag);
 
-  // Deletes the session associated with |session_tag| if it exists.
-  // Returns true if the session existed and was deleted, false otherwise.
-  bool DeleteSession(const std::string& session_tag);
-
   // Resets the tracking information for the session specified by |session_tag|.
   // This involves clearing all the windows and tabs from the session, while
   // keeping pointers saved in the synced_window_map_ and synced_tab_map_. Once
@@ -94,19 +96,6 @@
   // tabs not owned.
   void ResetSessionTracking(const std::string& session_tag);
 
-  // Tracks the deletion of a foreign tab by removing the given |tab_node_id|
-  // from the parent session. Doesn't actually remove any tab objects because
-  // the header may have or may not have already been updated to no longer
-  // parent this tab. Regardless, when the header is updated then cleanup will
-  // remove the actual tab data. However, this method always needs to be called
-  // upon foreign tab deletion, otherwise LookupTabNodeIds(...) may return
-  // already deleted tab node ids.
-  void DeleteForeignTab(const std::string& session_tag, int tab_node_id);
-
-  // Deletes those windows and tabs associated with |session_tag| that are no
-  // longer owned. See ResetSessionTracking(...).
-  void CleanupSession(const std::string& session_tag);
-
   // Adds the window with id |window_id| to the session specified by
   // |session_tag|. If none existed for that session, creates one. Similarly, if
   // the session did not exist yet, creates it. Ownership of the SessionWindow
@@ -125,18 +114,64 @@
                       SessionID::id_type tab_id,
                       size_t tab_index);
 
-  // Returns a pointer to the SessionTab object associated with |tab_id| for
-  // the session specified with |session_tag|. If none exists, creates one.
-  // Ownership of the SessionTab remains within the SyncedSessionTracker.
-  // |tab_node_id| must be a valid node id for the node backing this tab.
-  sessions::SessionTab* GetTab(const std::string& session_tag,
-                               SessionID::id_type tab_id,
-                               int tab_node_id);
+  // Adds |tab_node_id| to the session specified by |session_tag|, creating that
+  // session if necessary. This is necessary to ensure that each session has an
+  // up to date list of tab nodes linked to it for session deletion purposes.
+  // Note that this won't update the local tab pool, even if the local session
+  // tag is passed. The tab pool is only updated with new tab nodes when they're
+  // associated with a tab id (see ReassociateLocalTabNode or GetTabNodeForTab).
+  void OnTabNodeSeen(const std::string& session_tag, int tab_node_id);
 
-  // Fills |tab_node_ids| with the tab node ids (see GetTab) for all the tabs*
-  // associated with the session having tag |session_tag|.
-  void LookupTabNodeIds(const std::string& session_tag,
-                        std::set<int>* tab_node_ids);
+  // Returns a pointer to the SessionTab object associated with
+  // |tab_id| for the session specified with |session_tag|.
+  // Note: Ownership of the SessionTab remains within the SyncedSessionTracker.
+  // TODO(zea): Replace SessionTab with a Sync specific wrapper.
+  // crbug.com/662597
+  sessions::SessionTab* GetTab(const std::string& session_tag,
+                               SessionID::id_type tab_id);
+
+  // **** Methods specific to foreign sessions. ****
+
+  // Tracks the deletion of a foreign tab by removing the given |tab_node_id|
+  // from the parent session. Doesn't actually remove any tab objects because
+  // the header may have or may not have already been updated to no longer
+  // parent this tab. Regardless, when the header is updated then cleanup will
+  // remove the actual tab data. However, this method always needs to be called
+  // upon foreign tab deletion, otherwise LookupTabNodeIds(...) may return
+  // already deleted tab node ids.
+  void DeleteForeignTab(const std::string& session_tag, int tab_node_id);
+
+  // Deletes the session associated with |session_tag| if it exists.
+  // Returns true if the session existed and was deleted, false otherwise.
+  bool DeleteForeignSession(const std::string& session_tag);
+
+  // Deletes those windows and tabs associated with |session_tag| that are no
+  // longer owned. See ResetSessionTracking(...)..
+  void CleanupForeignSession(const std::string& session_tag);
+
+  // **** Methods specific to the local session. ****
+
+  // Set the local session tag. Must be called before any other local session
+  // methods are invoked.
+  void SetLocalSessionTag(const std::string& local_session_tag);
+
+  // Similar to CleanupForeignSession, but also marks any unmapped tabs as free
+  // in the tab node pool and fills |deleted_node_ids| with the set of locally
+  // free tab nodes to be deleted.
+  void CleanupLocalTabs(std::set<int>* deleted_node_ids);
+
+  // Fills |tab_node_id| with a tab node for |tab_id|. Returns true if an
+  // existing tab node was found, false if there was none and one had to be
+  // created.
+  bool GetTabNodeForLocalTab(int tab_id, int* tab_node_id);
+
+  // Reassociates the tab denoted by |tab_node_id| with a new tab id, preserving
+  // any previous SessionTab object the node was associated with. This is useful
+  // on restart when sync needs to reassociate tabs from a previous session with
+  // newly restored tabs (and can be used in conjunction with PutTabInWindow).
+  void ReassociateLocalTab(int tab_node_id, SessionID::id_type new_tab_id);
+
+  // **** Methods for querying/manipulating overall state ****.
 
   // Free the memory for all dynamically allocated objects and clear the
   // tracking structures.
@@ -160,20 +195,21 @@
   }
 
  private:
-  // Implementation for GetTab(...) above, permits invalid tab_node_id.
-  sessions::SessionTab* GetTabImpl(const std::string& session_tag,
-                                   SessionID::id_type tab_id,
-                                   int tab_node_id);
+  friend class SessionsSyncManagerTest;
+  friend class SyncedSessionTrackerTest;
+
+  // Implementation of CleanupForeignSession/CleanupLocalTabs.
+  void CleanupSessionImpl(const std::string& session_tag);
 
   // The client of the sync sessions datatype.
   SyncSessionsClient* const sessions_client_;
 
-  // The mapping of tab/window ids to their SessionTab/SessionWindow objects.
+  // The mapping of tab/window to their SessionTab/SessionWindow objects.
   // The SessionTab/SessionWindow objects referred to may be owned either by the
   // session in the |synced_session_map_| or be temporarily unmapped and live in
   // the |unmapped_tabs_|/|unmapped_windows_| collections.
   //
-  // Map: session tag -> (tab/window id -> SessionTab*/SessionWindow*)
+  // Map: session tag -> (tab/window -> SessionTab*/SessionWindow*)
   std::map<std::string, std::map<SessionID::id_type, sessions::SessionTab*>>
       synced_tab_map_;
   std::map<std::string, std::map<SessionID::id_type, sessions::SessionWindow*>>
@@ -203,6 +239,9 @@
   // sessions.
   std::string local_session_tag_;
 
+  // Pool of used/available sync nodes associated with local tabs.
+  TabNodePool local_tab_pool_;
+
   DISALLOW_COPY_AND_ASSIGN(SyncedSessionTracker);
 };
 
diff --git a/components/sync_sessions/synced_session_tracker_unittest.cc b/components/sync_sessions/synced_session_tracker_unittest.cc
index 68c1f471..282baf7 100644
--- a/components/sync_sessions/synced_session_tracker_unittest.cc
+++ b/components/sync_sessions/synced_session_tracker_unittest.cc
@@ -17,6 +17,10 @@
 
 const char kValidUrl[] = "http://www.example.com";
 const char kInvalidUrl[] = "invalid.url";
+const char kTag[] = "tag";
+const char kTag2[] = "tag2";
+const char kTag3[] = "tag3";
+const char kTitle[] = "title";
 
 }  // namespace
 
@@ -26,6 +30,7 @@
   ~SyncedSessionTrackerTest() override {}
 
   SyncedSessionTracker* GetTracker() { return &tracker_; }
+  TabNodePool* GetTabNodePool() { return &tracker_.local_tab_pool_; }
 
  private:
   FakeSyncSessionsClient sessions_client_;
@@ -33,34 +38,34 @@
 };
 
 TEST_F(SyncedSessionTrackerTest, GetSession) {
-  SyncedSession* session1 = GetTracker()->GetSession("tag");
-  SyncedSession* session2 = GetTracker()->GetSession("tag2");
-  ASSERT_EQ(session1, GetTracker()->GetSession("tag"));
+  SyncedSession* session1 = GetTracker()->GetSession(kTag);
+  SyncedSession* session2 = GetTracker()->GetSession(kTag2);
+  ASSERT_EQ(session1, GetTracker()->GetSession(kTag));
   ASSERT_NE(session1, session2);
   // Should clean up memory on its own.
 }
 
 TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) {
-  sessions::SessionTab* tab = GetTracker()->GetTab("tag", 0, 0);
-  ASSERT_EQ(tab, GetTracker()->GetTab("tag", 0, 0));
+  sessions::SessionTab* tab = GetTracker()->GetTab(kTag, 0);
+  ASSERT_EQ(tab, GetTracker()->GetTab(kTag, 0));
   // Should clean up memory on its own.
 }
 
 TEST_F(SyncedSessionTrackerTest, PutWindowInSession) {
-  GetTracker()->PutWindowInSession("tag", 0);
-  SyncedSession* session = GetTracker()->GetSession("tag");
+  GetTracker()->PutWindowInSession(kTag, 0);
+  SyncedSession* session = GetTracker()->GetSession(kTag);
   ASSERT_EQ(1U, session->windows.size());
   // Should clean up memory on its own.
 }
 
 TEST_F(SyncedSessionTrackerTest, PutTabInWindow) {
-  GetTracker()->PutWindowInSession("tag", 10);
-  GetTracker()->PutTabInWindow("tag", 10, 15,
+  GetTracker()->PutWindowInSession(kTag, 10);
+  GetTracker()->PutTabInWindow(kTag, 10, 15,
                                0);  // win id 10, tab id 15, tab ind 0.
-  SyncedSession* session = GetTracker()->GetSession("tag");
+  SyncedSession* session = GetTracker()->GetSession(kTag);
   ASSERT_EQ(1U, session->windows.size());
   ASSERT_EQ(1U, session->windows[10]->tabs.size());
-  ASSERT_EQ(GetTracker()->GetTab("tag", 15, 1),
+  ASSERT_EQ(GetTracker()->GetTab(kTag, 15),
             session->windows[10]->tabs[0].get());
   // Should clean up memory on its own.
 }
@@ -69,28 +74,28 @@
   std::vector<const SyncedSession*> sessions;
   ASSERT_FALSE(GetTracker()->LookupAllForeignSessions(
       &sessions, SyncedSessionTracker::PRESENTABLE));
-  GetTracker()->GetSession("tag1");
-  GetTracker()->PutWindowInSession("tag1", 0);
-  GetTracker()->PutTabInWindow("tag1", 0, 15, 0);
-  sessions::SessionTab* tab = GetTracker()->GetTab("tag1", 15, 1);
+  GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 15, 0);
+  sessions::SessionTab* tab = GetTracker()->GetTab(kTag, 15);
   ASSERT_TRUE(tab);
   tab->navigations.push_back(
       sessions::SerializedNavigationEntryTestHelper::CreateNavigation(kValidUrl,
-                                                                      "title"));
-  GetTracker()->GetSession("tag2");
-  GetTracker()->GetSession("tag3");
-  GetTracker()->PutWindowInSession("tag3", 0);
-  GetTracker()->PutTabInWindow("tag3", 0, 15, 0);
-  tab = GetTracker()->GetTab("tag3", 15, 1);
+                                                                      kTitle));
+  GetTracker()->GetSession(kTag2);
+  GetTracker()->GetSession(kTag3);
+  GetTracker()->PutWindowInSession(kTag3, 0);
+  GetTracker()->PutTabInWindow(kTag3, 0, 15, 0);
+  tab = GetTracker()->GetTab(kTag3, 15);
   ASSERT_TRUE(tab);
   tab->navigations.push_back(
       sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
-          kInvalidUrl, "title"));
+          kInvalidUrl, kTitle));
   ASSERT_TRUE(GetTracker()->LookupAllForeignSessions(
       &sessions, SyncedSessionTracker::PRESENTABLE));
   // Only the session with a valid window and tab gets returned.
   ASSERT_EQ(1U, sessions.size());
-  ASSERT_EQ("tag1", sessions[0]->session_tag);
+  ASSERT_EQ(kTag, sessions[0]->session_tag);
 
   ASSERT_TRUE(GetTracker()->LookupAllForeignSessions(
       &sessions, SyncedSessionTracker::RAW));
@@ -99,15 +104,15 @@
 
 TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) {
   std::vector<const sessions::SessionWindow*> windows;
-  ASSERT_FALSE(GetTracker()->LookupSessionWindows("tag1", &windows));
-  GetTracker()->GetSession("tag1");
-  GetTracker()->PutWindowInSession("tag1", 0);
-  GetTracker()->PutWindowInSession("tag1", 2);
-  GetTracker()->GetSession("tag2");
-  GetTracker()->PutWindowInSession("tag2", 0);
-  GetTracker()->PutWindowInSession("tag2", 2);
-  ASSERT_TRUE(GetTracker()->LookupSessionWindows("tag1", &windows));
-  ASSERT_EQ(2U, windows.size());  // Only windows from tag1 session.
+  ASSERT_FALSE(GetTracker()->LookupSessionWindows(kTag, &windows));
+  GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutWindowInSession(kTag, 2);
+  GetTracker()->GetSession(kTag2);
+  GetTracker()->PutWindowInSession(kTag2, 0);
+  GetTracker()->PutWindowInSession(kTag2, 2);
+  ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows));
+  ASSERT_EQ(2U, windows.size());  // Only windows from kTag session.
   ASSERT_NE((sessions::SessionWindow*)nullptr, windows[0]);
   ASSERT_NE((sessions::SessionWindow*)nullptr, windows[1]);
   ASSERT_NE(windows[1], windows[0]);
@@ -115,40 +120,37 @@
 
 TEST_F(SyncedSessionTrackerTest, LookupSessionTab) {
   const sessions::SessionTab* tab;
-  ASSERT_FALSE(GetTracker()->LookupSessionTab("tag1", 5, &tab));
-  GetTracker()->GetSession("tag1");
-  GetTracker()->PutWindowInSession("tag1", 0);
-  GetTracker()->PutTabInWindow("tag1", 0, 5, 0);
-  ASSERT_TRUE(GetTracker()->LookupSessionTab("tag1", 5, &tab));
+  ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, 5, &tab));
+  GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 5, 0);
+  ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 5, &tab));
   ASSERT_NE((sessions::SessionTab*)nullptr, tab);
 }
 
 TEST_F(SyncedSessionTrackerTest, Complex) {
-  const std::string tag1 = "tag";
-  const std::string tag2 = "tag2";
-  const std::string tag3 = "tag3";
   std::vector<sessions::SessionTab *> tabs1, tabs2;
   sessions::SessionTab* temp_tab;
   ASSERT_TRUE(GetTracker()->Empty());
   ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
-  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(tag1));
-  tabs1.push_back(GetTracker()->GetTab(tag1, 0, 0));
-  tabs1.push_back(GetTracker()->GetTab(tag1, 1, 1));
-  tabs1.push_back(GetTracker()->GetTab(tag1, 2, 2));
-  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(1U, GetTracker()->num_synced_sessions());
-  temp_tab = GetTracker()->GetTab(tag1, 0, 0);  // Already created.
-  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(1U, GetTracker()->num_synced_sessions());
+  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
+  tabs1.push_back(GetTracker()->GetTab(kTag, 0));
+  tabs1.push_back(GetTracker()->GetTab(kTag, 1));
+  tabs1.push_back(GetTracker()->GetTab(kTag, 2));
+  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
+  temp_tab = GetTracker()->GetTab(kTag, 0);  // Already created.
+  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
   ASSERT_EQ(tabs1[0], temp_tab);
-  tabs2.push_back(GetTracker()->GetTab(tag2, 0, 0));
-  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(tag2));
-  ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
-  ASSERT_FALSE(GetTracker()->DeleteSession(tag3));
+  tabs2.push_back(GetTracker()->GetTab(kTag2, 0));
+  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
+  ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
+  ASSERT_FALSE(GetTracker()->DeleteForeignSession(kTag3));
 
-  SyncedSession* session = GetTracker()->GetSession(tag1);
-  SyncedSession* session2 = GetTracker()->GetSession(tag2);
-  SyncedSession* session3 = GetTracker()->GetSession(tag3);
+  SyncedSession* session = GetTracker()->GetSession(kTag);
+  SyncedSession* session2 = GetTracker()->GetSession(kTag2);
+  SyncedSession* session3 = GetTracker()->GetSession(kTag3);
   session3->device_type = SyncedSession::TYPE_OTHER;
   ASSERT_EQ(3U, GetTracker()->num_synced_sessions());
 
@@ -157,25 +159,25 @@
   ASSERT_TRUE(session3);
   ASSERT_NE(session, session2);
   ASSERT_NE(session2, session3);
-  ASSERT_TRUE(GetTracker()->DeleteSession(tag3));
+  ASSERT_TRUE(GetTracker()->DeleteForeignSession(kTag3));
   ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
 
-  GetTracker()->PutWindowInSession(tag1, 0);           // Create a window.
-  GetTracker()->PutTabInWindow(tag1, 0, 2, 0);         // No longer unmapped.
-  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(tag1));  // Has not changed.
+  GetTracker()->PutWindowInSession(kTag, 0);           // Create a window.
+  GetTracker()->PutTabInWindow(kTag, 0, 2, 0);         // No longer unmapped.
+  ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));  // Has not changed.
 
   const sessions::SessionTab* tab_ptr;
-  ASSERT_TRUE(GetTracker()->LookupSessionTab(tag1, 0, &tab_ptr));
+  ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 0, &tab_ptr));
   ASSERT_EQ(tab_ptr, tabs1[0]);
-  ASSERT_TRUE(GetTracker()->LookupSessionTab(tag1, 2, &tab_ptr));
+  ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 2, &tab_ptr));
   ASSERT_EQ(tab_ptr, tabs1[2]);
-  ASSERT_FALSE(GetTracker()->LookupSessionTab(tag1, 3, &tab_ptr));
+  ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, 3, &tab_ptr));
   ASSERT_FALSE(tab_ptr);
 
   std::vector<const sessions::SessionWindow*> windows;
-  ASSERT_TRUE(GetTracker()->LookupSessionWindows(tag1, &windows));
+  ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows));
   ASSERT_EQ(1U, windows.size());
-  ASSERT_TRUE(GetTracker()->LookupSessionWindows(tag2, &windows));
+  ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag2, &windows));
   ASSERT_EQ(0U, windows.size());
 
   // The sessions don't have valid tabs, lookup should not succeed.
@@ -187,8 +189,8 @@
   ASSERT_EQ(2U, sessions.size());
 
   GetTracker()->Clear();
-  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(tag2));
+  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag2));
   ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
 }
 
@@ -203,107 +205,101 @@
       // More attempts than tabs means we'll sometimes get the same tabs,
       // sometimes have to allocate new tabs.
       int rand_tab_num = base::RandInt(0, kMaxTabs);
-      sessions::SessionTab* tab =
-          GetTracker()->GetTab(tag, rand_tab_num, rand_tab_num + 1);
+      sessions::SessionTab* tab = GetTracker()->GetTab(tag, rand_tab_num + 1);
       ASSERT_TRUE(tab);
     }
   }
 }
 
-TEST_F(SyncedSessionTrackerTest, LookupTabNodeIds) {
+TEST_F(SyncedSessionTrackerTest, LookupForeignTabNodeIds) {
   std::set<int> result;
-  std::string tag1 = "session1";
-  std::string tag2 = "session2";
-  std::string tag3 = "session3";
 
-  GetTracker()->GetTab(tag1, 1, 1);
-  GetTracker()->GetTab(tag1, 2, 2);
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->OnTabNodeSeen(kTag, 1);
+  GetTracker()->OnTabNodeSeen(kTag, 2);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(2U, result.size());
   EXPECT_FALSE(result.end() == result.find(1));
   EXPECT_FALSE(result.end() == result.find(2));
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_TRUE(result.empty());
 
-  GetTracker()->PutWindowInSession(tag1, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 3, 0);
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 3, 0);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(2U, result.size());
 
-  GetTracker()->GetTab(tag1, 3, 3);
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->OnTabNodeSeen(kTag, 3);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(3U, result.size());
   EXPECT_FALSE(result.end() == result.find(3));
 
-  GetTracker()->GetTab(tag2, 1, 21);
-  GetTracker()->GetTab(tag2, 2, 22);
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->OnTabNodeSeen(kTag2, 21);
+  GetTracker()->OnTabNodeSeen(kTag2, 22);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_EQ(2U, result.size());
   EXPECT_FALSE(result.end() == result.find(21));
   EXPECT_FALSE(result.end() == result.find(22));
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(3U, result.size());
   EXPECT_FALSE(result.end() == result.find(1));
   EXPECT_FALSE(result.end() == result.find(2));
 
-  GetTracker()->LookupTabNodeIds(tag3, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag3, &result);
   EXPECT_TRUE(result.empty());
-  GetTracker()->PutWindowInSession(tag3, 1);
-  GetTracker()->PutTabInWindow(tag3, 1, 5, 0);
-  GetTracker()->LookupTabNodeIds(tag3, &result);
+  GetTracker()->PutWindowInSession(kTag3, 1);
+  GetTracker()->PutTabInWindow(kTag3, 1, 5, 0);
+  GetTracker()->LookupForeignTabNodeIds(kTag3, &result);
   EXPECT_TRUE(result.empty());
-  EXPECT_FALSE(GetTracker()->DeleteSession(tag3));
-  GetTracker()->LookupTabNodeIds(tag3, &result);
+  EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag3));
+  GetTracker()->LookupForeignTabNodeIds(kTag3, &result);
   EXPECT_TRUE(result.empty());
 
-  EXPECT_FALSE(GetTracker()->DeleteSession(tag1));
-  GetTracker()->LookupTabNodeIds(tag1, &result);
+  EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag));
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_TRUE(result.empty());
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_EQ(2U, result.size());
   EXPECT_FALSE(result.end() == result.find(21));
   EXPECT_FALSE(result.end() == result.find(22));
 
-  GetTracker()->GetTab(tag2, 1, 21);
-  GetTracker()->GetTab(tag2, 2, 23);
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  GetTracker()->OnTabNodeSeen(kTag2, 21);
+  GetTracker()->OnTabNodeSeen(kTag2, 23);
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_EQ(3U, result.size());
   EXPECT_FALSE(result.end() == result.find(21));
   EXPECT_FALSE(result.end() == result.find(22));
   EXPECT_FALSE(result.end() == result.find(23));
 
-  EXPECT_FALSE(GetTracker()->DeleteSession(tag2));
-  GetTracker()->LookupTabNodeIds(tag2, &result);
+  EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag2));
+  GetTracker()->LookupForeignTabNodeIds(kTag2, &result);
   EXPECT_TRUE(result.empty());
 }
 
 TEST_F(SyncedSessionTrackerTest, SessionTracking) {
   ASSERT_TRUE(GetTracker()->Empty());
-  std::string tag1 = "tag1";
-  std::string tag2 = "tag2";
 
   // Create some session information that is stale.
-  SyncedSession* session1 = GetTracker()->GetSession(tag1);
-  GetTracker()->PutWindowInSession(tag1, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 0, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 1, 1);
-  GetTracker()->GetTab(tag1, 2, 3U)->window_id.set_id(0);  // Will be unmapped.
-  GetTracker()->GetTab(tag1, 3, 4U)->window_id.set_id(0);  // Will be unmapped.
-  GetTracker()->PutWindowInSession(tag1, 1);
-  GetTracker()->PutTabInWindow(tag1, 1, 4, 0);
-  GetTracker()->PutTabInWindow(tag1, 1, 5, 1);
+  SyncedSession* session1 = GetTracker()->GetSession(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 0, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 1, 1);
+  GetTracker()->GetTab(kTag, 2)->window_id.set_id(0);  // Will be unmapped.
+  GetTracker()->GetTab(kTag, 3)->window_id.set_id(0);  // Will be unmapped.
+  GetTracker()->PutWindowInSession(kTag, 1);
+  GetTracker()->PutTabInWindow(kTag, 1, 4, 0);
+  GetTracker()->PutTabInWindow(kTag, 1, 5, 1);
   ASSERT_EQ(2U, session1->windows.size());
   ASSERT_EQ(2U, session1->windows[0]->tabs.size());
   ASSERT_EQ(2U, session1->windows[1]->tabs.size());
-  ASSERT_EQ(6U, GetTracker()->num_synced_tabs(tag1));
+  ASSERT_EQ(6U, GetTracker()->num_synced_tabs(kTag));
 
   // Create a session that should not be affected.
-  SyncedSession* session2 = GetTracker()->GetSession(tag2);
-  GetTracker()->PutWindowInSession(tag2, 2);
-  GetTracker()->PutTabInWindow(tag2, 2, 1, 0);
+  SyncedSession* session2 = GetTracker()->GetSession(kTag2);
+  GetTracker()->PutWindowInSession(kTag2, 2);
+  GetTracker()->PutTabInWindow(kTag2, 2, 1, 0);
   ASSERT_EQ(1U, session2->windows.size());
   ASSERT_EQ(1U, session2->windows[2]->tabs.size());
-  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(tag2));
+  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
 
   // Reset tracking and get the current windows/tabs.
   // We simulate moving a tab from one window to another, then closing the
@@ -311,18 +307,18 @@
   // on the remaining window.
 
   // New tab, arrived before meta node so unmapped.
-  GetTracker()->GetTab(tag1, 6, 7U);
-  GetTracker()->ResetSessionTracking(tag1);
-  GetTracker()->PutWindowInSession(tag1, 0);
-  GetTracker()->PutTabInWindow(tag1, 0, 0, 0);
+  GetTracker()->GetTab(kTag, 6);
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, 0);
+  GetTracker()->PutTabInWindow(kTag, 0, 0, 0);
   // Tab 1 is closed.
-  GetTracker()->PutTabInWindow(tag1, 0, 2, 1);  // No longer unmapped.
+  GetTracker()->PutTabInWindow(kTag, 0, 2, 1);  // No longer unmapped.
   // Tab 3 was unmapped and does not get used.
-  GetTracker()->PutTabInWindow(tag1, 0, 4, 2);  // Moved from window 1.
+  GetTracker()->PutTabInWindow(kTag, 0, 4, 2);  // Moved from window 1.
   // Window 1 was closed, along with tab 5.
-  GetTracker()->PutTabInWindow(tag1, 0, 6, 3);  // No longer unmapped.
+  GetTracker()->PutTabInWindow(kTag, 0, 6, 3);  // No longer unmapped.
   // Session 2 should not be affected.
-  GetTracker()->CleanupSession(tag1);
+  GetTracker()->CleanupForeignSession(kTag);
 
   // Verify that only those parts of the session not owned have been removed.
   ASSERT_EQ(1U, session1->windows.size());
@@ -330,39 +326,171 @@
   ASSERT_EQ(1U, session2->windows.size());
   ASSERT_EQ(1U, session2->windows[2]->tabs.size());
   ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
-  ASSERT_EQ(4U, GetTracker()->num_synced_tabs(tag1));
-  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(tag2));
+  ASSERT_EQ(4U, GetTracker()->num_synced_tabs(kTag));
+  ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
 
   // All memory should be properly deallocated by destructor for the
   // SyncedSessionTracker.
 }
 
 TEST_F(SyncedSessionTrackerTest, DeleteForeignTab) {
-  std::string session_tag = "session_tag";
-  int tab_id_1 = 1;
-  int tab_id_2 = 2;
-  int tab_node_id_3 = 3;
-  int tab_node_id_4 = 4;
+  int tab_node_id_1 = 1;
+  int tab_node_id_2 = 2;
   std::set<int> result;
 
-  GetTracker()->GetTab(session_tag, tab_id_1, tab_node_id_3);
-  GetTracker()->GetTab(session_tag, tab_id_1, tab_node_id_4);
-  GetTracker()->GetTab(session_tag, tab_id_2, tab_node_id_3);
-  GetTracker()->GetTab(session_tag, tab_id_2, tab_node_id_4);
+  GetTracker()->OnTabNodeSeen(kTag, tab_node_id_1);
+  GetTracker()->OnTabNodeSeen(kTag, tab_node_id_2);
 
-  GetTracker()->LookupTabNodeIds(session_tag, &result);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(2U, result.size());
-  EXPECT_TRUE(result.find(tab_node_id_3) != result.end());
-  EXPECT_TRUE(result.find(tab_node_id_4) != result.end());
+  EXPECT_TRUE(result.find(tab_node_id_1) != result.end());
+  EXPECT_TRUE(result.find(tab_node_id_2) != result.end());
 
-  GetTracker()->DeleteForeignTab(session_tag, tab_node_id_3);
-  GetTracker()->LookupTabNodeIds(session_tag, &result);
+  GetTracker()->DeleteForeignTab(kTag, tab_node_id_1);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_EQ(1U, result.size());
-  EXPECT_TRUE(result.find(tab_node_id_4) != result.end());
+  EXPECT_TRUE(result.find(tab_node_id_2) != result.end());
 
-  GetTracker()->DeleteForeignTab(session_tag, tab_node_id_4);
-  GetTracker()->LookupTabNodeIds(session_tag, &result);
+  GetTracker()->DeleteForeignTab(kTag, tab_node_id_2);
+  GetTracker()->LookupForeignTabNodeIds(kTag, &result);
   EXPECT_TRUE(result.empty());
 }
 
+TEST_F(SyncedSessionTrackerTest, CleanupLocalTabs) {
+  std::set<int> free_node_ids;
+  int tab_node_id = TabNodePool::kInvalidTabNodeID;
+  const int kWindow1 = 1;
+  const int kTabNode1 = 1;
+  const int kTabNode2 = 2;
+  const int kTabNode3 = 3;
+  const int kTab1 = 15;
+  const int kTab2 = 25;
+  const int kTab3 = 35;
+  const int kTabIndex = 0;
+
+  GetTracker()->SetLocalSessionTag(kTag);
+
+  // Start with two restored tab nodes.
+  GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
+  GetTracker()->ReassociateLocalTab(kTabNode2, kTab2);
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+  EXPECT_FALSE(GetTabNodePool()->Full());
+  EXPECT_EQ(2U, GetTabNodePool()->Capacity());
+
+  // Associate with no tabs. The tab pool should now be full.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+  EXPECT_TRUE(GetTabNodePool()->Full());
+
+  // Associate with only 1 tab open. A tab node should be reused.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1, kTabIndex);
+  EXPECT_TRUE(GetTracker()->GetTabNodeForLocalTab(kTab1, &tab_node_id));
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+
+  // TabNodePool should have one free tab node and one used.
+  EXPECT_EQ(2U, GetTabNodePool()->Capacity());
+  EXPECT_FALSE(GetTabNodePool()->Empty());
+  EXPECT_FALSE(GetTabNodePool()->Full());
+
+  // Simulate a tab opening, which should use the last free tab node.
+  EXPECT_TRUE(GetTracker()->GetTabNodeForLocalTab(kTab2, &tab_node_id));
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+
+  // Simulate another tab opening, which should create a new associated tab
+  // node.
+  EXPECT_FALSE(GetTracker()->GetTabNodeForLocalTab(kTab3, &tab_node_id));
+  EXPECT_EQ(kTabNode3, tab_node_id);
+  EXPECT_EQ(3U, GetTabNodePool()->Capacity());
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+
+  // Fetching the same tab should return the same tab node id.
+  EXPECT_TRUE(GetTracker()->GetTabNodeForLocalTab(kTab3, &tab_node_id));
+  EXPECT_EQ(kTabNode3, tab_node_id);
+  EXPECT_TRUE(GetTabNodePool()->Empty());
+
+  // Associate with no tabs. All tabs should be freed again, and the pool
+  // should now be full.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+  EXPECT_TRUE(GetTabNodePool()->Full());
+  EXPECT_FALSE(GetTabNodePool()->Empty());
+}
+
+TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
+  std::set<int> free_node_ids;
+  const int kWindow1 = 1;
+  const int kTabNode = 0;
+  const int kTabIndex = 0;
+  const int kTab1 = 15;
+  const int kTab2 = 25;
+
+  // First create the tab normally.
+  GetTracker()->SetLocalSessionTag(kTag);
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
+
+  // Map it to a window with the same tab id as it was created with.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1, kTabIndex);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  SyncedSession* session = GetTracker()->GetSession(kTag);
+  ASSERT_EQ(1U, session->windows.size());
+  ASSERT_EQ(1U, session->windows[kWindow1]->tabs.size());
+  ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
+            session->windows[kWindow1]->tabs[0].get());
+
+  // Then reassociate with a new tab id.
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
+
+  // Reset tracking, and put the new tab id into the window.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2, kTabIndex);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+
+  // Now that it's been mapped, it should be accessible both via the
+  // GetSession as well as the GetTab.
+  ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
+            session->windows[kWindow1]->tabs[0].get());
+  ASSERT_EQ(session->tab_node_ids.size(),
+            session->tab_node_ids.count(kTabNode));
+  ASSERT_EQ(1U, GetTabNodePool()->Capacity());
+}
+
+TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
+  std::set<int> free_node_ids;
+  const int kWindow1 = 1;
+  const int kTabNode = 0;
+  const int kTabIndex = 0;
+  const int kTab1 = 15;
+  const int kTab2 = 25;
+
+  // First create the old tab in an unmapped state.
+  GetTracker()->SetLocalSessionTag(kTag);
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
+
+  // Map it to a window, but reassociated with a new tab id.
+  GetTracker()->ResetSessionTracking(kTag);
+  GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
+  GetTracker()->PutWindowInSession(kTag, kWindow1);
+  GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2, kTabIndex);
+  GetTracker()->CleanupLocalTabs(&free_node_ids);
+  EXPECT_TRUE(free_node_ids.empty());
+
+  // Now that it's been mapped, it should be accessible both via the
+  // GetSession as well as GetTab.
+  SyncedSession* session = GetTracker()->GetSession(kTag);
+  ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
+            session->windows[kWindow1]->tabs[0].get());
+  ASSERT_EQ(session->tab_node_ids.size(),
+            session->tab_node_ids.count(kTabNode));
+  ASSERT_EQ(1U, GetTabNodePool()->Capacity());
+}
+
 }  // namespace sync_sessions
diff --git a/components/sync_sessions/tab_node_pool.cc b/components/sync_sessions/tab_node_pool.cc
index 4067b92..f187d23d 100644
--- a/components/sync_sessions/tab_node_pool.cc
+++ b/components/sync_sessions/tab_node_pool.cc
@@ -4,12 +4,10 @@
 
 #include "components/sync_sessions/tab_node_pool.h"
 
-#include "base/format_macros.h"
+#include <algorithm>
+
 #include "base/logging.h"
-#include "base/strings/stringprintf.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_data.h"
 #include "components/sync/protocol/session_specifics.pb.h"
 #include "components/sync/protocol/sync.pb.h"
 
@@ -26,114 +24,91 @@
 
 TabNodePool::~TabNodePool() {}
 
-// Static
-std::string TabNodePool::TabIdToTag(const std::string& machine_tag,
-                                    int tab_node_id) {
-  return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id);
-}
-
 void TabNodePool::AddTabNode(int tab_node_id) {
   DCHECK_GT(tab_node_id, kInvalidTabNodeID);
   DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
-  unassociated_nodes_.insert(tab_node_id);
-  if (max_used_tab_node_id_ < tab_node_id)
-    max_used_tab_node_id_ = tab_node_id;
+  DVLOG(1) << "Adding tab node " << tab_node_id << " to pool.";
+  max_used_tab_node_id_ = std::max(max_used_tab_node_id_, tab_node_id);
+  free_nodes_pool_.insert(tab_node_id);
 }
 
 void TabNodePool::AssociateTabNode(int tab_node_id, SessionID::id_type tab_id) {
   DCHECK_GT(tab_node_id, kInvalidTabNodeID);
-  // Remove sync node if it is in unassociated nodes pool.
-  std::set<int>::iterator u_it = unassociated_nodes_.find(tab_node_id);
-  if (u_it != unassociated_nodes_.end()) {
-    unassociated_nodes_.erase(u_it);
-  } else {
-    // This is a new node association, the sync node should be free.
-    // Remove node from free node pool and then associate it with the tab.
-    std::set<int>::iterator it = free_nodes_pool_.find(tab_node_id);
-    DCHECK(it != free_nodes_pool_.end());
-    free_nodes_pool_.erase(it);
-  }
+  DCHECK_GT(tab_id, kInvalidTabID);
+
+  // This is a new node association, the sync node should be free.
+  // Remove node from free node pool and then associate it with the tab.
+  std::set<int>::iterator it = free_nodes_pool_.find(tab_node_id);
+  DCHECK(it != free_nodes_pool_.end());
+  free_nodes_pool_.erase(it);
+
   DCHECK(nodeid_tabid_map_.find(tab_node_id) == nodeid_tabid_map_.end());
+  DVLOG(1) << "Associating tab node " << tab_node_id << " with tab " << tab_id;
   nodeid_tabid_map_[tab_node_id] = tab_id;
+  tabid_nodeid_map_[tab_id] = tab_node_id;
 }
 
-int TabNodePool::GetFreeTabNode(syncer::SyncChangeList* append_changes) {
-  DCHECK_GT(machine_tag_.length(), 0U);
-  DCHECK(append_changes);
+bool TabNodePool::GetTabNodeForTab(SessionID::id_type tab_id,
+                                   int* tab_node_id) {
+  if (tabid_nodeid_map_.find(tab_id) != tabid_nodeid_map_.end()) {
+    *tab_node_id = tabid_nodeid_map_[tab_id];
+    return true;
+  }
+
   if (free_nodes_pool_.empty()) {
     // Tab pool has no free nodes, allocate new one.
-    int tab_node_id = ++max_used_tab_node_id_;
-    std::string tab_node_tag = TabIdToTag(machine_tag_, tab_node_id);
+    *tab_node_id = ++max_used_tab_node_id_;
+    AddTabNode(*tab_node_id);
 
-    // We fill the new node with just enough data so that in case of a crash/bug
-    // we can identify the node as our own on re-association and reuse it.
-    sync_pb::EntitySpecifics entity;
-    sync_pb::SessionSpecifics* specifics = entity.mutable_session();
-    specifics->set_session_tag(machine_tag_);
-    specifics->set_tab_node_id(tab_node_id);
-    append_changes->push_back(syncer::SyncChange(
-        FROM_HERE, syncer::SyncChange::ACTION_ADD,
-        syncer::SyncData::CreateLocalData(tab_node_tag, tab_node_tag, entity)));
-
-    // Grow the pool by 1 since we created a new node.
-    DVLOG(1) << "Adding sync node " << tab_node_id << " to tab node id pool";
-    free_nodes_pool_.insert(tab_node_id);
-    return tab_node_id;
+    AssociateTabNode(*tab_node_id, tab_id);
+    return false;
   } else {
     // Return the next free node.
-    return *free_nodes_pool_.begin();
+    *tab_node_id = *free_nodes_pool_.begin();
+    AssociateTabNode(*tab_node_id, tab_id);
+    return true;
   }
 }
 
-void TabNodePool::FreeTabNode(int tab_node_id,
-                              syncer::SyncChangeList* append_changes) {
-  DCHECK(append_changes);
-  TabNodeIDToTabIDMap::iterator it = nodeid_tabid_map_.find(tab_node_id);
-  DCHECK(it != nodeid_tabid_map_.end());
-  nodeid_tabid_map_.erase(it);
-  FreeTabNodeInternal(tab_node_id, append_changes);
-}
+void TabNodePool::FreeTab(int tab_id) {
+  DCHECK_GT(tab_id, kInvalidTabID);
+  TabIDToTabNodeIDMap::iterator it = tabid_nodeid_map_.find(tab_id);
+  if (it == tabid_nodeid_map_.end()) {
+    return;  // Already freed.
+  }
 
-void TabNodePool::FreeTabNodeInternal(int tab_node_id,
-                                      syncer::SyncChangeList* append_changes) {
-  DCHECK(free_nodes_pool_.find(tab_node_id) == free_nodes_pool_.end());
-  DCHECK(append_changes);
+  int tab_node_id = it->second;
+  DVLOG(1) << "Freeing tab " << tab_id << " at node " << tab_node_id;
+  nodeid_tabid_map_.erase(nodeid_tabid_map_.find(tab_node_id));
+  tabid_nodeid_map_.erase(it);
   free_nodes_pool_.insert(tab_node_id);
-
-  // If number of free nodes exceed kFreeNodesHighWatermark,
-  // delete sync nodes till number reaches kFreeNodesLowWatermark.
-  // Note: This logic is to mitigate temporary disassociation issues with old
-  // clients: http://crbug.com/259918. Newer versions do not need this.
-  if (free_nodes_pool_.size() > kFreeNodesHighWatermark) {
-    for (std::set<int>::iterator free_it = free_nodes_pool_.begin();
-         free_it != free_nodes_pool_.end();) {
-      const std::string tab_node_tag = TabIdToTag(machine_tag_, *free_it);
-      append_changes->push_back(syncer::SyncChange(
-          FROM_HERE, syncer::SyncChange::ACTION_DELETE,
-          syncer::SyncData::CreateLocalDelete(tab_node_tag, syncer::SESSIONS)));
-      free_nodes_pool_.erase(free_it++);
-      if (free_nodes_pool_.size() <= kFreeNodesLowWatermark) {
-        return;
-      }
-    }
-  }
-}
-
-bool TabNodePool::IsUnassociatedTabNode(int tab_node_id) {
-  return unassociated_nodes_.find(tab_node_id) != unassociated_nodes_.end();
 }
 
 void TabNodePool::ReassociateTabNode(int tab_node_id,
                                      SessionID::id_type tab_id) {
-  // Remove from list of unassociated sync_nodes if present.
-  std::set<int>::iterator it = unassociated_nodes_.find(tab_node_id);
-  if (it != unassociated_nodes_.end()) {
-    unassociated_nodes_.erase(it);
-  } else {
-    // tab_node_id must be an already associated node.
-    DCHECK(nodeid_tabid_map_.find(tab_node_id) != nodeid_tabid_map_.end());
+  DCHECK_GT(tab_node_id, kInvalidTabNodeID);
+  DCHECK_GT(tab_id, kInvalidTabID);
+
+  auto tabid_it = tabid_nodeid_map_.find(tab_id);
+  if (tabid_it != tabid_nodeid_map_.end()) {
+    if (tabid_it->second == tab_node_id) {
+      return;  // Already associated properly.
+    } else {
+      // Another node is already associated with this tab. Free it.
+      FreeTab(tab_id);
+    }
   }
-  nodeid_tabid_map_[tab_node_id] = tab_id;
+
+  auto nodeid_it = nodeid_tabid_map_.find(tab_node_id);
+  if (nodeid_it != nodeid_tabid_map_.end()) {
+    // This node was already associated with another tab. Free it.
+    FreeTab(nodeid_it->second);
+  } else {
+    // This is a new tab node. Add it before association.
+    AddTabNode(tab_node_id);
+  }
+
+  AssociateTabNode(tab_node_id, tab_id);
 }
 
 SessionID::id_type TabNodePool::GetTabIdFromTabNodeId(int tab_node_id) const {
@@ -144,27 +119,33 @@
   return kInvalidTabID;
 }
 
-void TabNodePool::DeleteUnassociatedTabNodes(
-    syncer::SyncChangeList* append_changes) {
-  for (std::set<int>::iterator it = unassociated_nodes_.begin();
-       it != unassociated_nodes_.end();) {
-    FreeTabNodeInternal(*it, append_changes);
-    unassociated_nodes_.erase(it++);
+void TabNodePool::CleanupTabNodes(std::set<int>* deleted_node_ids) {
+  // If number of free nodes exceed kFreeNodesHighWatermark,
+  // delete sync nodes till number reaches kFreeNodesLowWatermark.
+  // Note: This logic is to mitigate temporary disassociation issues with old
+  // clients: http://crbug.com/259918. Newer versions do not need this.
+  if (free_nodes_pool_.size() > kFreeNodesHighWatermark) {
+    for (std::set<int>::iterator free_it = free_nodes_pool_.begin();
+         free_it != free_nodes_pool_.end();) {
+      deleted_node_ids->insert(*free_it);
+      free_nodes_pool_.erase(free_it++);
+      if (free_nodes_pool_.size() <= kFreeNodesLowWatermark) {
+        return;
+      }
+    }
   }
-  DCHECK(unassociated_nodes_.empty());
 }
 
 // Clear tab pool.
 void TabNodePool::Clear() {
-  unassociated_nodes_.clear();
   free_nodes_pool_.clear();
   nodeid_tabid_map_.clear();
+  tabid_nodeid_map_.clear();
   max_used_tab_node_id_ = kInvalidTabNodeID;
 }
 
 size_t TabNodePool::Capacity() const {
-  return nodeid_tabid_map_.size() + unassociated_nodes_.size() +
-         free_nodes_pool_.size();
+  return nodeid_tabid_map_.size() + free_nodes_pool_.size();
 }
 
 bool TabNodePool::Empty() const {
@@ -175,8 +156,4 @@
   return nodeid_tabid_map_.empty();
 }
 
-void TabNodePool::SetMachineTag(const std::string& machine_tag) {
-  machine_tag_ = machine_tag;
-}
-
 }  // namespace sync_sessions
diff --git a/components/sync_sessions/tab_node_pool.h b/components/sync_sessions/tab_node_pool.h
index caadc4e..833daf7 100644
--- a/components/sync_sessions/tab_node_pool.h
+++ b/components/sync_sessions/tab_node_pool.h
@@ -13,11 +13,6 @@
 
 #include "base/macros.h"
 #include "components/sessions/core/session_id.h"
-#include "components/sync/model/sync_change_processor.h"
-
-namespace syncer {
-class SyncChangeProcessor;
-}  // namespace syncer
 
 namespace sync_sessions {
 
@@ -27,17 +22,11 @@
 // - a tab_id: created by session service, unique to this client
 // - a tab_node_id: the id for a particular sync tab node. This is used
 //   to generate the sync tab node tag through:
-//       tab_tag = StringPrintf("%s_%ui", local_session_tag, tab_node_id);
+//       tab_tag = StringPrintf("%s %d", local_session_tag, tab_node_id);
 //
-// A sync node can be in one of the three states:
+// A sync node can be in one of the two states:
 // 1. Associated   : Sync node is used and associated with a tab.
-// 2. Unassociated : Sync node is used but currently unassociated with any tab.
-//                   This is true for old nodes that remain from a session
-//                   restart. Nodes are only unassociated temporarily while the
-//                   model associator figures out which tabs belong to which
-//                   nodes. Eventually any remaining unassociated nodes are
-//                   freed.
-// 3. Free         : Sync node is unused.
+// 2. Free         : Sync node is unused.
 
 class TabNodePool {
  public:
@@ -54,65 +43,37 @@
 
   static const int kInvalidTabNodeID;
 
-  // Build a sync tag from tab_node_id.
-  static std::string TabIdToTag(const std::string& machine_tag,
-                                int tab_node_id);
-
-  // Returns the tab_node_id for the next free tab node. If none are available,
-  // creates a new tab node and adds it to free nodes pool. The free node can
-  // then be used to associate with a tab by calling AssociateTabNode.
-  // Note: The node is considered free until it has been associated. Repeated
-  // calls to GetFreeTabNode will return the same id until node has been
-  // associated.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller context. If the need
-  // to create nodes arises in the implementation, associated SyncChanges will
-  // be appended to this list for later application by the caller via the
-  // SyncChangeProcessor.
-  int GetFreeTabNode(syncer::SyncChangeList* change_output);
-
-  // Removes association for |tab_node_id| and returns it to the free node pool.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller's context. If the need
-  // to delete sync nodes arises in the implementation, associated SyncChanges
-  // will be appended to this list for later application by the caller via the
-  // SyncChangeProcessor.
-  void FreeTabNode(int tab_node_id, syncer::SyncChangeList* change_output);
-
-  // Associates |tab_node_id| with |tab_id|. |tab_node_id| should either be
-  // unassociated or free. If |tab_node_id| is free, |tab_node_id| is removed
-  // from the free node pool In order to associate a non free sync node,
-  // use ReassociateTabNode.
-  void AssociateTabNode(int tab_node_id, SessionID::id_type tab_id);
-
-  // Adds |tab_node_id| as an unassociated sync node.
-  // Note: this should only be called when we discover tab sync nodes from
-  // previous sessions, not for freeing tab nodes we created through
-  // GetFreeTabNode (use FreeTabNode below for that).
-  void AddTabNode(int tab_node_id);
+  // Fills |tab_node_id| with a tab node associated with |tab_id|.
+  // If tab_id is already associated with a tab_node_id, reuses the existing
+  // association. Otherwise attempts to get the next free tab node and
+  // associate it with |tab_id|. If none are available, will create a new tab
+  // node.
+  // Returns true if a pre-existing tab node could be reused, false if a new one
+  // had to be created.
+  bool GetTabNodeForTab(SessionID::id_type tab_id, int* tab_node_id);
 
   // Returns the tab_id for |tab_node_id| if it is associated else returns
   // kInvalidTabID.
   SessionID::id_type GetTabIdFromTabNodeId(int tab_node_id) const;
 
-  // Reassociates |tab_node_id| with |tab_id|. |tab_node_id| must be either
-  // associated with a tab or in the set of unassociated nodes.
+  // Reassociates |tab_node_id| with |tab_id|. If |tab_node_id| is not already
+  // known, it is added to the tab node pool before being associated.
   void ReassociateTabNode(int tab_node_id, SessionID::id_type tab_id);
 
-  // Returns true if |tab_node_id| is an unassociated tab node.
-  bool IsUnassociatedTabNode(int tab_node_id);
+  // Removes association for |tab_id| and returns its tab node to the free node
+  // pool.
+  void FreeTab(int tab_id);
 
-  // Returns any unassociated nodes to the free node pool.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller's context.
-  // See FreeTabNode for more detail.
-  void DeleteUnassociatedTabNodes(syncer::SyncChangeList* change_output);
+  // Fills |deleted_node_ids| with any free nodes to be deleted as proscribed
+  // by the free node low/high watermarks, in order to ensure the free node pool
+  // does not grow too large.
+  void CleanupTabNodes(std::set<int>* deleted_node_ids);
 
   // Clear tab pool.
   void Clear();
 
   // Return the number of tab nodes this client currently has allocated
-  // (including both free, unassociated and associated nodes)
+  // (including both free and associated nodes).
   size_t Capacity() const;
 
   // Return empty status (all tab nodes are in use).
@@ -121,41 +82,36 @@
   // Return full status (no tab nodes are in use).
   bool Full();
 
-  void SetMachineTag(const std::string& machine_tag);
-
  private:
   friend class SyncTabNodePoolTest;
   typedef std::map<int, SessionID::id_type> TabNodeIDToTabIDMap;
+  typedef std::map<SessionID::id_type, int> TabIDToTabNodeIDMap;
 
-  // Adds |tab_node_id| to free node pool.
-  // |change_output| *must* be provided. It is the TabNodePool's link to
-  // the SyncChange pipeline that exists in the caller's context.
-  // See FreeTabNode for more detail.
-  void FreeTabNodeInternal(int tab_node_id,
-                           syncer::SyncChangeList* change_output);
+  // Adds |tab_node_id| to the tab node pool.
+  // Note: this should only be called when we discover tab sync nodes from
+  // previous sessions, not for freeing tab nodes we created through
+  // GetTabNodeForTab (use FreeTab for that).
+  void AddTabNode(int tab_node_id);
+
+  // Associates |tab_node_id| with |tab_id|. |tab_node_id| must be free. In
+  // order to associated a non-free tab node, ReassociateTabNode must be
+  // used.
+  void AssociateTabNode(int tab_node_id, SessionID::id_type tab_id);
 
   // Stores mapping of node ids associated with tab_ids, these are the used
   // nodes of tab node pool.
   // The nodes in the map can be returned to free tab node pool by calling
-  // FreeTabNode(tab_node_id).
+  // FreeTab(..).
   TabNodeIDToTabIDMap nodeid_tabid_map_;
+  TabIDToTabNodeIDMap tabid_nodeid_map_;
 
   // The node ids for the set of free sync nodes.
   std::set<int> free_nodes_pool_;
 
-  // The node ids that are added to pool using AddTabNode and are currently
-  // not associated with any tab. They can be reassociated using
-  // ReassociateTabNode.
-  std::set<int> unassociated_nodes_;
-
   // The maximum used tab_node id for a sync node. A new sync node will always
   // be created with max_used_tab_node_id_ + 1.
   int max_used_tab_node_id_;
 
-  // The machine tag associated with this tab pool. Used in the title of new
-  // sync nodes.
-  std::string machine_tag_;
-
   DISALLOW_COPY_AND_ASSIGN(TabNodePool);
 };
 
diff --git a/components/sync_sessions/tab_node_pool_unittest.cc b/components/sync_sessions/tab_node_pool_unittest.cc
index cd125a2..591c8b54 100644
--- a/components/sync_sessions/tab_node_pool_unittest.cc
+++ b/components/sync_sessions/tab_node_pool_unittest.cc
@@ -6,16 +6,13 @@
 
 #include <vector>
 
-#include "components/sync/model/sync_change.h"
-#include "components/sync/protocol/session_specifics.pb.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace sync_sessions {
 
 class SyncTabNodePoolTest : public testing::Test {
  protected:
-  SyncTabNodePoolTest() { pool_.SetMachineTag("tag"); }
+  SyncTabNodePoolTest() {}
 
   int GetMaxUsedTabNodeId() const { return pool_.max_used_tab_node_id_; }
 
@@ -32,107 +29,127 @@
 
 namespace {
 
+const int kTabNodeId1 = 10;
+const int kTabNodeId2 = 5;
+const int kTabNodeId3 = 1000;
+const int kTabId1 = 1;
+const int kTabId2 = 2;
+const int kTabId3 = 3;
+
 TEST_F(SyncTabNodePoolTest, TabNodeIdIncreases) {
-  syncer::SyncChangeList changes;
+  std::set<int> deleted_node_ids;
+
   // max_used_tab_node_ always increases.
-  pool_.AddTabNode(10);
-  EXPECT_EQ(10, GetMaxUsedTabNodeId());
-  pool_.AddTabNode(5);
-  EXPECT_EQ(10, GetMaxUsedTabNodeId());
-  pool_.AddTabNode(1000);
-  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
-  pool_.ReassociateTabNode(1000, 1);
-  pool_.ReassociateTabNode(5, 2);
-  pool_.ReassociateTabNode(10, 3);
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  EXPECT_EQ(kTabNodeId1, GetMaxUsedTabNodeId());
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId2);
+  EXPECT_EQ(kTabNodeId1, GetMaxUsedTabNodeId());
+  pool_.ReassociateTabNode(kTabNodeId3, kTabId3);
+  EXPECT_EQ(kTabNodeId3, GetMaxUsedTabNodeId());
   // Freeing a tab node does not change max_used_tab_node_id_.
-  pool_.FreeTabNode(1000, &changes);
-  EXPECT_TRUE(changes.empty());
-  pool_.FreeTabNode(5, &changes);
-  EXPECT_TRUE(changes.empty());
-  pool_.FreeTabNode(10, &changes);
-  EXPECT_TRUE(changes.empty());
+  pool_.FreeTab(kTabId3);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  pool_.FreeTab(kTabId2);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  pool_.FreeTab(kTabId1);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
   for (int i = 0; i < 3; ++i) {
-    pool_.AssociateTabNode(pool_.GetFreeTabNode(&changes), i + 1);
-    EXPECT_EQ(1000, GetMaxUsedTabNodeId());
+    int tab_node_id = -1;
+    EXPECT_TRUE(pool_.GetTabNodeForTab(i + 1, &tab_node_id));
+    EXPECT_EQ(kTabNodeId3, GetMaxUsedTabNodeId());
   }
-  EXPECT_TRUE(changes.empty());
-  EXPECT_EQ(1000, GetMaxUsedTabNodeId());
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  EXPECT_EQ(kTabNodeId3, GetMaxUsedTabNodeId());
   EXPECT_TRUE(pool_.Empty());
 }
 
-TEST_F(SyncTabNodePoolTest, OldTabNodesAddAndRemove) {
-  syncer::SyncChangeList changes;
-  // VerifyOldTabNodes are added.
-  pool_.AddTabNode(1);
-  pool_.AddTabNode(2);
-  EXPECT_EQ(2u, pool_.Capacity());
+TEST_F(SyncTabNodePoolTest, Reassociation) {
+  // Reassociate tab node 1 with tab id 1.
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  EXPECT_EQ(1U, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(1));
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(2));
-  pool_.ReassociateTabNode(1, 2);
-  EXPECT_TRUE(pool_.Empty());
-  pool_.AssociateTabNode(2, 3);
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(1));
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(2));
-  pool_.FreeTabNode(2, &changes);
-  EXPECT_TRUE(changes.empty());
-  // 2 should be returned to free node pool_.
-  EXPECT_EQ(2u, pool_.Capacity());
-  // Should be able to free 1.
-  pool_.FreeTabNode(1, &changes);
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Introduce a new tab node associated with the same tab. The old tab node
+  // should get added to the free pool
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId1);
+  EXPECT_EQ(2U, pool_.Capacity());
   EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  pool_.AssociateTabNode(1, 1);
-  EXPECT_EQ(2, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  pool_.AssociateTabNode(2, 1);
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Reassociating the same tab node/tab should have no effect.
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId1);
+  EXPECT_EQ(2U, pool_.Capacity());
+  EXPECT_FALSE(pool_.Empty());
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Reassociating the new tab node with a new tab should just update the
+  // association tables.
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId2);
+  EXPECT_EQ(2U, pool_.Capacity());
+  EXPECT_FALSE(pool_.Empty());
+  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
+            pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId2, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
+
+  // Reassociating the first tab node should make the pool empty.
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  EXPECT_EQ(2U, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
   EXPECT_FALSE(pool_.Full());
-  EXPECT_FALSE(pool_.Full());
+  EXPECT_EQ(kTabId1, pool_.GetTabIdFromTabNodeId(kTabNodeId1));
+  EXPECT_EQ(kTabId2, pool_.GetTabIdFromTabNodeId(kTabNodeId2));
 }
 
-TEST_F(SyncTabNodePoolTest, OldTabNodesReassociation) {
-  // VerifyOldTabNodes are reassociated correctly.
-  pool_.AddTabNode(4);
-  pool_.AddTabNode(5);
-  pool_.AddTabNode(6);
+TEST_F(SyncTabNodePoolTest, ReassociateThenFree) {
+  std::set<int> deleted_node_ids;
+
+  // Verify old tab nodes are reassociated correctly.
+  pool_.ReassociateTabNode(kTabNodeId1, kTabId1);
+  pool_.ReassociateTabNode(kTabNodeId2, kTabId2);
+  pool_.ReassociateTabNode(kTabNodeId3, kTabId3);
   EXPECT_EQ(3u, pool_.Capacity());
   EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.IsUnassociatedTabNode(4));
-  pool_.ReassociateTabNode(4, 5);
-  pool_.AssociateTabNode(5, 6);
-  pool_.AssociateTabNode(6, 7);
-  // Free 5 and 6.
-  syncer::SyncChangeList changes;
-  pool_.FreeTabNode(5, &changes);
-  pool_.FreeTabNode(6, &changes);
-  EXPECT_TRUE(changes.empty());
-  // 5 and 6 nodes should not be unassociated.
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(5));
-  EXPECT_FALSE(pool_.IsUnassociatedTabNode(6));
-  // Free node pool should have 5 and 6.
+  // Free tabs 2 and 3.
+  pool_.FreeTab(kTabId2);
+  pool_.FreeTab(kTabId3);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
+  // Free node pool should have 2 and 3.
   EXPECT_FALSE(pool_.Empty());
   EXPECT_EQ(3u, pool_.Capacity());
 
   // Free all nodes
-  pool_.FreeTabNode(4, &changes);
-  EXPECT_TRUE(changes.empty());
+  pool_.FreeTab(kTabId1);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_TRUE(deleted_node_ids.empty());
   EXPECT_TRUE(pool_.Full());
   std::set<int> free_sync_ids;
   for (int i = 0; i < 3; ++i) {
-    free_sync_ids.insert(pool_.GetFreeTabNode(&changes));
-    // GetFreeTabNode will return the same value till the node is
-    // reassociated.
-    pool_.AssociateTabNode(pool_.GetFreeTabNode(&changes), i + 1);
+    int tab_node_id = -1;
+    EXPECT_TRUE(pool_.GetTabNodeForTab(i, &tab_node_id));
+    free_sync_ids.insert(tab_node_id);
   }
 
   EXPECT_TRUE(pool_.Empty());
   EXPECT_EQ(3u, free_sync_ids.size());
-  EXPECT_EQ(1u, free_sync_ids.count(4));
-  EXPECT_EQ(1u, free_sync_ids.count(5));
-  EXPECT_EQ(1u, free_sync_ids.count(6));
+  EXPECT_EQ(1u, free_sync_ids.count(kTabNodeId1));
+  EXPECT_EQ(1u, free_sync_ids.count(kTabNodeId2));
+  EXPECT_EQ(1u, free_sync_ids.count(kTabNodeId3));
 }
 
 TEST_F(SyncTabNodePoolTest, Init) {
@@ -141,106 +158,49 @@
 }
 
 TEST_F(SyncTabNodePoolTest, AddGet) {
-  syncer::SyncChangeList changes;
   int free_nodes[] = {5, 10};
   AddFreeTabNodes(2, free_nodes);
 
   EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_EQ(5, pool_.GetFreeTabNode(&changes));
-  pool_.AssociateTabNode(5, 1);
+  int tab_node_id = -1;
+  EXPECT_TRUE(pool_.GetTabNodeForTab(1, &tab_node_id));
+  EXPECT_EQ(5, tab_node_id);
   EXPECT_FALSE(pool_.Empty());
   EXPECT_FALSE(pool_.Full());
   EXPECT_EQ(2U, pool_.Capacity());
   // 5 is now used, should return 10.
-  EXPECT_EQ(10, pool_.GetFreeTabNode(&changes));
+  EXPECT_TRUE(pool_.GetTabNodeForTab(2, &tab_node_id));
+  EXPECT_EQ(10, tab_node_id);
 }
 
-TEST_F(SyncTabNodePoolTest, All) {
-  syncer::SyncChangeList changes;
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(0U, pool_.Capacity());
-
-  // GetFreeTabNode returns the lowest numbered free node.
-  EXPECT_EQ(0, pool_.GetFreeTabNode(&changes));
-  EXPECT_EQ(1U, changes.size());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(1U, pool_.Capacity());
-
-  // Associate 5, next free node should be 10.
-  pool_.AssociateTabNode(0, 1);
-  EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
-  EXPECT_EQ(2U, changes.size());
-  changes.clear();
-  pool_.AssociateTabNode(1, 2);
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_FALSE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  // Release them in reverse order.
-  pool_.FreeTabNode(1, &changes);
-  pool_.FreeTabNode(0, &changes);
-  EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(0, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  pool_.AssociateTabNode(0, 1);
-  EXPECT_EQ(2U, pool_.Capacity());
-  EXPECT_EQ(1, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes.empty());
-  pool_.AssociateTabNode(1, 2);
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_FALSE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  // Release them again.
-  pool_.FreeTabNode(1, &changes);
-  pool_.FreeTabNode(0, &changes);
-  EXPECT_FALSE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(2U, pool_.Capacity());
-  pool_.Clear();
-  EXPECT_TRUE(pool_.Empty());
-  EXPECT_TRUE(pool_.Full());
-  EXPECT_EQ(0U, pool_.Capacity());
-}
-
-TEST_F(SyncTabNodePoolTest, GetFreeTabNodeCreate) {
-  syncer::SyncChangeList changes;
-  EXPECT_EQ(0, pool_.GetFreeTabNode(&changes));
-  EXPECT_TRUE(changes[0].IsValid());
-  EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
-  EXPECT_TRUE(changes[0].sync_data().IsValid());
-  sync_pb::EntitySpecifics entity = changes[0].sync_data().GetSpecifics();
-  sync_pb::SessionSpecifics specifics(entity.session());
-  EXPECT_EQ(0, specifics.tab_node_id());
+TEST_F(SyncTabNodePoolTest, GetTabNodeForTabCreate) {
+  int tab_node_id = -1;
+  EXPECT_FALSE(pool_.GetTabNodeForTab(1, &tab_node_id));
+  EXPECT_EQ(0, tab_node_id);
 }
 
 TEST_F(SyncTabNodePoolTest, TabPoolFreeNodeLimits) {
+  std::set<int> deleted_node_ids;
+
   // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that
   // freeing the last node reduces the free node pool size to
   // kFreeNodesLowWatermark.
-  syncer::SyncChangeList changes;
   SessionID session_id;
   std::vector<int> used_sync_ids;
   for (size_t i = 1; i <= TabNodePool::kFreeNodesHighWatermark + 1; ++i) {
     session_id.set_id(i);
-    int sync_id = pool_.GetFreeTabNode(&changes);
-    pool_.AssociateTabNode(sync_id, i);
+    int sync_id = -1;
+    EXPECT_FALSE(pool_.GetTabNodeForTab(i, &sync_id));
     used_sync_ids.push_back(sync_id);
   }
 
   // Free all except one node.
-  int last_sync_id = used_sync_ids.back();
   used_sync_ids.pop_back();
 
-  for (size_t i = 0; i < used_sync_ids.size(); ++i) {
-    pool_.FreeTabNode(used_sync_ids[i], &changes);
+  for (size_t i = 1; i <= used_sync_ids.size(); ++i) {
+    pool_.FreeTab(i);
+    pool_.CleanupTabNodes(&deleted_node_ids);
+    EXPECT_TRUE(deleted_node_ids.empty());
   }
 
   // Except one node all nodes should be in FreeNode pool.
@@ -251,7 +211,11 @@
 
   // Freeing the last sync node should drop the free nodes to
   // kFreeNodesLowWatermark.
-  pool_.FreeTabNode(last_sync_id, &changes);
+  pool_.FreeTab(TabNodePool::kFreeNodesHighWatermark + 1);
+  pool_.CleanupTabNodes(&deleted_node_ids);
+  EXPECT_EQ(TabNodePool::kFreeNodesHighWatermark + 1 -
+                TabNodePool::kFreeNodesLowWatermark,
+            deleted_node_ids.size());
   EXPECT_FALSE(pool_.Empty());
   EXPECT_TRUE(pool_.Full());
   EXPECT_EQ(TabNodePool::kFreeNodesLowWatermark, pool_.Capacity());