Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/logging.h"
      6 #include "base/stl_util.h"
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "chrome/browser/sync/glue/synced_session_tracker.h"
      9 
     10 namespace browser_sync {
     11 
     12 SyncedSessionTracker::SyncedSessionTracker() {
     13 }
     14 
     15 SyncedSessionTracker::~SyncedSessionTracker() {
     16   Clear();
     17 }
     18 
     19 void SyncedSessionTracker::SetLocalSessionTag(
     20     const std::string& local_session_tag) {
     21   local_session_tag_ = local_session_tag;
     22 }
     23 
     24 bool SyncedSessionTracker::LookupAllForeignSessions(
     25     std::vector<const SyncedSession*>* sessions) const {
     26   DCHECK(sessions);
     27   sessions->clear();
     28   // Fill vector of sessions from our synced session map.
     29   for (SyncedSessionMap::const_iterator i =
     30     synced_session_map_.begin(); i != synced_session_map_.end(); ++i) {
     31     // Only include foreign sessions with open tabs.
     32     SyncedSession* foreign_session = i->second;
     33     if (i->first != local_session_tag_ && !foreign_session->windows.empty()) {
     34       bool found_tabs = false;
     35       for (SyncedSession::SyncedWindowMap::const_iterator iter =
     36                foreign_session->windows.begin();
     37            iter != foreign_session->windows.end(); ++iter) {
     38         if (!SessionWindowHasNoTabsToSync(*(iter->second))) {
     39           found_tabs = true;
     40           break;
     41         }
     42       }
     43       if (found_tabs)
     44         sessions->push_back(foreign_session);
     45     }
     46   }
     47 
     48   return !sessions->empty();
     49 }
     50 
     51 bool SyncedSessionTracker::LookupSessionWindows(
     52     const std::string& session_tag,
     53     std::vector<const SessionWindow*>* windows) const {
     54   DCHECK(windows);
     55   windows->clear();
     56   SyncedSessionMap::const_iterator iter = synced_session_map_.find(session_tag);
     57   if (iter == synced_session_map_.end())
     58     return false;
     59   windows->clear();
     60   for (SyncedSession::SyncedWindowMap::const_iterator window_iter =
     61            iter->second->windows.begin();
     62        window_iter != iter->second->windows.end(); window_iter++) {
     63     windows->push_back(window_iter->second);
     64   }
     65   return true;
     66 }
     67 
     68 bool SyncedSessionTracker::LookupSessionTab(
     69     const std::string& tag,
     70     SessionID::id_type tab_id,
     71     const SessionTab** tab) const {
     72   DCHECK(tab);
     73   SyncedTabMap::const_iterator tab_map_iter = synced_tab_map_.find(tag);
     74   if (tab_map_iter == synced_tab_map_.end()) {
     75     // We have no record of this session.
     76     *tab = NULL;
     77     return false;
     78   }
     79   IDToSessionTabMap::const_iterator tab_iter =
     80       tab_map_iter->second.find(tab_id);
     81   if (tab_iter == tab_map_iter->second.end()) {
     82     // We have no record of this tab.
     83     *tab = NULL;
     84     return false;
     85   }
     86   *tab = tab_iter->second.tab_ptr;
     87   return true;
     88 }
     89 
     90 bool SyncedSessionTracker::LookupTabNodeIds(
     91     const std::string& session_tag, std::set<int>* tab_node_ids) {
     92   tab_node_ids->clear();
     93   SyncedTabMap::const_iterator tab_map_iter =
     94       synced_tab_map_.find(session_tag);
     95   if (tab_map_iter == synced_tab_map_.end())
     96     return false;
     97 
     98   IDToSessionTabMap::const_iterator tab_iter = tab_map_iter->second.begin();
     99   while (tab_iter != tab_map_iter->second.end()) {
    100     if (tab_iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID)
    101       tab_node_ids->insert(tab_iter->second.tab_node_id);
    102     ++tab_iter;
    103   }
    104   return true;
    105 }
    106 
    107 bool SyncedSessionTracker::LookupLocalSession(const SyncedSession** output)
    108     const {
    109   SyncedSessionMap::const_iterator it =
    110       synced_session_map_.find(local_session_tag_);
    111   if (it != synced_session_map_.end()) {
    112     *output = it->second;
    113     return true;
    114   }
    115   return false;
    116 }
    117 
    118 SyncedSession* SyncedSessionTracker::GetSession(
    119     const std::string& session_tag) {
    120   SyncedSession* synced_session = NULL;
    121   if (synced_session_map_.find(session_tag) !=
    122       synced_session_map_.end()) {
    123     synced_session = synced_session_map_[session_tag];
    124   } else {
    125     synced_session = new SyncedSession;
    126     DVLOG(1) << "Creating new session with tag " << session_tag << " at "
    127              << synced_session;
    128     synced_session->session_tag = session_tag;
    129     synced_session_map_[session_tag] = synced_session;
    130   }
    131   DCHECK(synced_session);
    132   return synced_session;
    133 }
    134 
    135 bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
    136   bool found_session = false;
    137   SyncedSessionMap::iterator iter = synced_session_map_.find(session_tag);
    138   if (iter != synced_session_map_.end()) {
    139     SyncedSession* session = iter->second;
    140     synced_session_map_.erase(iter);
    141     delete session;  // Delete the SyncedSession object.
    142     found_session = true;
    143   }
    144   synced_window_map_.erase(session_tag);
    145   // It's possible there was no header node but there were tab nodes.
    146   if (synced_tab_map_.erase(session_tag) > 0) {
    147     found_session = true;
    148   }
    149   return found_session;
    150 }
    151 
    152 void SyncedSessionTracker::ResetSessionTracking(
    153     const std::string& session_tag) {
    154   // Reset window tracking.
    155   GetSession(session_tag)->windows.clear();
    156   SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
    157   if (window_iter != synced_window_map_.end()) {
    158     for (IDToSessionWindowMap::iterator window_map_iter =
    159              window_iter->second.begin();
    160          window_map_iter != window_iter->second.end(); ++window_map_iter) {
    161       window_map_iter->second.owned = false;
    162       // We clear out the tabs to prevent double referencing of the same tab.
    163       // All tabs that are in use will be added back as needed.
    164       window_map_iter->second.window_ptr->tabs.clear();
    165     }
    166   }
    167 
    168   // Reset tab tracking.
    169   SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
    170   if (tab_iter != synced_tab_map_.end()) {
    171     for (IDToSessionTabMap::iterator tab_map_iter =
    172              tab_iter->second.begin();
    173          tab_map_iter != tab_iter->second.end(); ++tab_map_iter) {
    174       tab_map_iter->second.owned = false;
    175     }
    176   }
    177 }
    178 
    179 bool SyncedSessionTracker::DeleteOldSessionWindowIfNecessary(
    180     SessionWindowWrapper window_wrapper) {
    181   // Clear the tabs first, since we don't want the destructor to destroy
    182   // them. Their deletion will be handled by DeleteOldSessionTab below.
    183   if (!window_wrapper.owned) {
    184     DVLOG(1) << "Deleting closed window "
    185              << window_wrapper.window_ptr->window_id.id();
    186     window_wrapper.window_ptr->tabs.clear();
    187     delete window_wrapper.window_ptr;
    188     return true;
    189   }
    190   return false;
    191 }
    192 
    193 bool SyncedSessionTracker::DeleteOldSessionTabIfNecessary(
    194     SessionTabWrapper tab_wrapper) {
    195   if (!tab_wrapper.owned) {
    196     if (VLOG_IS_ON(1)) {
    197       SessionTab* tab_ptr = tab_wrapper.tab_ptr;
    198       std::string title;
    199       if (tab_ptr->navigations.size() > 0) {
    200         title = " (" + base::UTF16ToUTF8(
    201             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
    202       }
    203       DVLOG(1) << "Deleting closed tab " << tab_ptr->tab_id.id() << title
    204                << " from window " << tab_ptr->window_id.id();
    205     }
    206     unmapped_tabs_.erase(tab_wrapper.tab_ptr);
    207     delete tab_wrapper.tab_ptr;
    208     return true;
    209   }
    210   return false;
    211 }
    212 
    213 void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
    214   // Go through and delete any windows or tabs without owners.
    215   SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
    216   if (window_iter != synced_window_map_.end()) {
    217     for (IDToSessionWindowMap::iterator iter = window_iter->second.begin();
    218          iter != window_iter->second.end();) {
    219       SessionWindowWrapper window_wrapper = iter->second;
    220       if (DeleteOldSessionWindowIfNecessary(window_wrapper))
    221         window_iter->second.erase(iter++);
    222       else
    223         ++iter;
    224     }
    225   }
    226 
    227   SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
    228   if (tab_iter != synced_tab_map_.end()) {
    229     for (IDToSessionTabMap::iterator iter = tab_iter->second.begin();
    230          iter != tab_iter->second.end();) {
    231       SessionTabWrapper tab_wrapper = iter->second;
    232       if (DeleteOldSessionTabIfNecessary(tab_wrapper))
    233         tab_iter->second.erase(iter++);
    234       else
    235         ++iter;
    236     }
    237   }
    238 }
    239 
    240 void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
    241                                               SessionID::id_type window_id) {
    242   SessionWindow* window_ptr = NULL;
    243   IDToSessionWindowMap::iterator iter =
    244       synced_window_map_[session_tag].find(window_id);
    245   if (iter != synced_window_map_[session_tag].end()) {
    246     iter->second.owned = true;
    247     window_ptr = iter->second.window_ptr;
    248     DVLOG(1) << "Putting seen window " << window_id  << " at " << window_ptr
    249              << "in " << (session_tag == local_session_tag_ ?
    250                           "local session" : session_tag);
    251   } else {
    252     // Create the window.
    253     window_ptr = new SessionWindow();
    254     window_ptr->window_id.set_id(window_id);
    255     synced_window_map_[session_tag][window_id] =
    256         SessionWindowWrapper(window_ptr, IS_OWNED);
    257     DVLOG(1) << "Putting new window " << window_id  << " at " << window_ptr
    258              << "in " << (session_tag == local_session_tag_ ?
    259                           "local session" : session_tag);
    260   }
    261   DCHECK(window_ptr);
    262   DCHECK_EQ(window_ptr->window_id.id(), window_id);
    263   DCHECK_EQ(reinterpret_cast<SessionWindow*>(NULL),
    264             GetSession(session_tag)->windows[window_id]);
    265   GetSession(session_tag)->windows[window_id] = window_ptr;
    266 }
    267 
    268 void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
    269                                           SessionID::id_type window_id,
    270                                           SessionID::id_type tab_id,
    271                                           size_t tab_index) {
    272   // We're called here for two reasons. 1) We've received an update to the
    273   // SessionWindow information of a SessionHeader node for a foreign session,
    274   // and 2) The SessionHeader node for our local session changed. In both cases
    275   // we need to update our tracking state to reflect the change.
    276   //
    277   // Because the SessionHeader nodes are separate from the individual tab nodes
    278   // and we don't store tab_node_ids in the header / SessionWindow specifics,
    279   // the tab_node_ids are not always available when processing headers.
    280   // We know that we will eventually process (via GetTab) every single tab node
    281   // in the system, so we permit ourselves to use kInvalidTabNodeID here and
    282   // rely on the later update to build the mapping (or a restart).
    283   // TODO(tim): Bug 98892. Update comment when Sync API conversion finishes to
    284   // mention that in the meantime, the only ill effect is that we may not be
    285   // able to fully clean up a stale foreign session, but it will get garbage
    286   // collected eventually.
    287   SessionTab* tab_ptr = GetTabImpl(
    288       session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
    289   unmapped_tabs_.erase(tab_ptr);
    290   synced_tab_map_[session_tag][tab_id].owned = true;
    291   tab_ptr->window_id.set_id(window_id);
    292   DVLOG(1) << "  - tab " << tab_id << " added to window "<< window_id;
    293   DCHECK(GetSession(session_tag)->windows.find(window_id) !=
    294          GetSession(session_tag)->windows.end());
    295   std::vector<SessionTab*>& window_tabs =
    296       GetSession(session_tag)->windows[window_id]->tabs;
    297   if (window_tabs.size() <= tab_index) {
    298     window_tabs.resize(tab_index+1, NULL);
    299   }
    300   DCHECK(!window_tabs[tab_index]);
    301   window_tabs[tab_index] = tab_ptr;
    302 }
    303 
    304 SessionTab* SyncedSessionTracker::GetTab(
    305     const std::string& session_tag,
    306     SessionID::id_type tab_id,
    307     int tab_node_id) {
    308   DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
    309   return GetTabImpl(session_tag, tab_id, tab_node_id);
    310 }
    311 
    312 SessionTab* SyncedSessionTracker::GetTabImpl(
    313     const std::string& session_tag,
    314     SessionID::id_type tab_id,
    315     int tab_node_id) {
    316   SessionTab* tab_ptr = NULL;
    317   IDToSessionTabMap::iterator iter =
    318       synced_tab_map_[session_tag].find(tab_id);
    319   if (iter != synced_tab_map_[session_tag].end()) {
    320     tab_ptr = iter->second.tab_ptr;
    321     if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
    322         tab_id != TabNodePool::kInvalidTabID) {
    323       // TabIDs are not stable across restarts of a client. Consider this
    324       // example with two tabs:
    325       //
    326       // http://a.com  TabID1 --> NodeIDA
    327       // http://b.com  TabID2 --> NodeIDB
    328       //
    329       // After restart, tab ids are reallocated. e.g, one possibility:
    330       // http://a.com TabID2 --> NodeIDA
    331       // http://b.com TabID1 --> NodeIDB
    332       //
    333       // If that happend on a remote client, here we will see an update to
    334       // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
    335       // with tab_node_id changing from NodeIDB to NodeIDA.
    336       //
    337       // We can also wind up here if we created this tab as an out-of-order
    338       // update to the header node for this session before actually associating
    339       // the tab itself, so the tab node id wasn't available at the time and
    340       // is currenlty kInvalidTabNodeID.
    341       //
    342       // In both cases, we update the tab_node_id.
    343       iter->second.tab_node_id = tab_node_id;
    344     }
    345 
    346     if (VLOG_IS_ON(1)) {
    347       std::string title;
    348       if (tab_ptr->navigations.size() > 0) {
    349         title = " (" + base::UTF16ToUTF8(
    350             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
    351       }
    352       DVLOG(1) << "Getting "
    353                << (session_tag == local_session_tag_ ?
    354                    "local session" : session_tag)
    355                << "'s seen tab " << tab_id  << " at " << tab_ptr << title;
    356     }
    357   } else {
    358     tab_ptr = new SessionTab();
    359     tab_ptr->tab_id.set_id(tab_id);
    360     synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
    361                                                              NOT_OWNED,
    362                                                              tab_node_id);
    363     unmapped_tabs_.insert(tab_ptr);
    364     DVLOG(1) << "Getting "
    365              << (session_tag == local_session_tag_ ?
    366                  "local session" : session_tag)
    367              << "'s new tab " << tab_id  << " at " << tab_ptr;
    368   }
    369   DCHECK(tab_ptr);
    370   DCHECK_EQ(tab_ptr->tab_id.id(), tab_id);
    371   return tab_ptr;
    372 }
    373 
    374 void SyncedSessionTracker::Clear() {
    375   // Delete SyncedSession objects (which also deletes all their windows/tabs).
    376   STLDeleteValues(&synced_session_map_);
    377 
    378   // Go through and delete any tabs we had allocated but had not yet placed into
    379   // a SyncedSessionobject.
    380   STLDeleteElements(&unmapped_tabs_);
    381 
    382   // Get rid of our Window/Tab maps (does not delete the actual Window/Tabs
    383   // themselves; they should have all been deleted above).
    384   synced_window_map_.clear();
    385   synced_tab_map_.clear();
    386 
    387   local_session_tag_.clear();
    388 }
    389 
    390 }  // namespace browser_sync
    391