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 SyncedSession* SyncedSessionTracker::GetSession(
    108     const std::string& session_tag) {
    109   SyncedSession* synced_session = NULL;
    110   if (synced_session_map_.find(session_tag) !=
    111       synced_session_map_.end()) {
    112     synced_session = synced_session_map_[session_tag];
    113   } else {
    114     synced_session = new SyncedSession;
    115     DVLOG(1) << "Creating new session with tag " << session_tag << " at "
    116              << synced_session;
    117     synced_session->session_tag = session_tag;
    118     synced_session_map_[session_tag] = synced_session;
    119   }
    120   DCHECK(synced_session);
    121   return synced_session;
    122 }
    123 
    124 bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
    125   bool found_session = false;
    126   SyncedSessionMap::iterator iter = synced_session_map_.find(session_tag);
    127   if (iter != synced_session_map_.end()) {
    128     SyncedSession* session = iter->second;
    129     synced_session_map_.erase(iter);
    130     delete session;  // Delete the SyncedSession object.
    131     found_session = true;
    132   }
    133   synced_window_map_.erase(session_tag);
    134   // It's possible there was no header node but there were tab nodes.
    135   if (synced_tab_map_.erase(session_tag) > 0) {
    136     found_session = true;
    137   }
    138   return found_session;
    139 }
    140 
    141 void SyncedSessionTracker::ResetSessionTracking(
    142     const std::string& session_tag) {
    143   // Reset window tracking.
    144   GetSession(session_tag)->windows.clear();
    145   SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
    146   if (window_iter != synced_window_map_.end()) {
    147     for (IDToSessionWindowMap::iterator window_map_iter =
    148              window_iter->second.begin();
    149          window_map_iter != window_iter->second.end(); ++window_map_iter) {
    150       window_map_iter->second.owned = false;
    151       // We clear out the tabs to prevent double referencing of the same tab.
    152       // All tabs that are in use will be added back as needed.
    153       window_map_iter->second.window_ptr->tabs.clear();
    154     }
    155   }
    156 
    157   // Reset tab tracking.
    158   SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
    159   if (tab_iter != synced_tab_map_.end()) {
    160     for (IDToSessionTabMap::iterator tab_map_iter =
    161              tab_iter->second.begin();
    162          tab_map_iter != tab_iter->second.end(); ++tab_map_iter) {
    163       tab_map_iter->second.owned = false;
    164     }
    165   }
    166 }
    167 
    168 bool SyncedSessionTracker::DeleteOldSessionWindowIfNecessary(
    169     SessionWindowWrapper window_wrapper) {
    170   // Clear the tabs first, since we don't want the destructor to destroy
    171   // them. Their deletion will be handled by DeleteOldSessionTab below.
    172   if (!window_wrapper.owned) {
    173     DVLOG(1) << "Deleting closed window "
    174              << window_wrapper.window_ptr->window_id.id();
    175     window_wrapper.window_ptr->tabs.clear();
    176     delete window_wrapper.window_ptr;
    177     return true;
    178   }
    179   return false;
    180 }
    181 
    182 bool SyncedSessionTracker::DeleteOldSessionTabIfNecessary(
    183     SessionTabWrapper tab_wrapper) {
    184   if (!tab_wrapper.owned) {
    185     if (VLOG_IS_ON(1)) {
    186       SessionTab* tab_ptr = tab_wrapper.tab_ptr;
    187       std::string title;
    188       if (tab_ptr->navigations.size() > 0) {
    189         title = " (" + UTF16ToUTF8(
    190             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
    191       }
    192       DVLOG(1) << "Deleting closed tab " << tab_ptr->tab_id.id() << title
    193                << " from window " << tab_ptr->window_id.id();
    194     }
    195     unmapped_tabs_.erase(tab_wrapper.tab_ptr);
    196     delete tab_wrapper.tab_ptr;
    197     return true;
    198   }
    199   return false;
    200 }
    201 
    202 void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
    203   // Go through and delete any windows or tabs without owners.
    204   SyncedWindowMap::iterator window_iter = synced_window_map_.find(session_tag);
    205   if (window_iter != synced_window_map_.end()) {
    206     for (IDToSessionWindowMap::iterator iter = window_iter->second.begin();
    207          iter != window_iter->second.end();) {
    208       SessionWindowWrapper window_wrapper = iter->second;
    209       if (DeleteOldSessionWindowIfNecessary(window_wrapper))
    210         window_iter->second.erase(iter++);
    211       else
    212         ++iter;
    213     }
    214   }
    215 
    216   SyncedTabMap::iterator tab_iter = synced_tab_map_.find(session_tag);
    217   if (tab_iter != synced_tab_map_.end()) {
    218     for (IDToSessionTabMap::iterator iter = tab_iter->second.begin();
    219          iter != tab_iter->second.end();) {
    220       SessionTabWrapper tab_wrapper = iter->second;
    221       if (DeleteOldSessionTabIfNecessary(tab_wrapper))
    222         tab_iter->second.erase(iter++);
    223       else
    224         ++iter;
    225     }
    226   }
    227 }
    228 
    229 void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
    230                                               SessionID::id_type window_id) {
    231   SessionWindow* window_ptr = NULL;
    232   IDToSessionWindowMap::iterator iter =
    233       synced_window_map_[session_tag].find(window_id);
    234   if (iter != synced_window_map_[session_tag].end()) {
    235     iter->second.owned = true;
    236     window_ptr = iter->second.window_ptr;
    237     DVLOG(1) << "Putting seen window " << window_id  << " at " << window_ptr
    238              << "in " << (session_tag == local_session_tag_ ?
    239                           "local session" : session_tag);
    240   } else {
    241     // Create the window.
    242     window_ptr = new SessionWindow();
    243     window_ptr->window_id.set_id(window_id);
    244     synced_window_map_[session_tag][window_id] =
    245         SessionWindowWrapper(window_ptr, IS_OWNED);
    246     DVLOG(1) << "Putting new window " << window_id  << " at " << window_ptr
    247              << "in " << (session_tag == local_session_tag_ ?
    248                           "local session" : session_tag);
    249   }
    250   DCHECK(window_ptr);
    251   DCHECK_EQ(window_ptr->window_id.id(), window_id);
    252   DCHECK_EQ(reinterpret_cast<SessionWindow*>(NULL),
    253             GetSession(session_tag)->windows[window_id]);
    254   GetSession(session_tag)->windows[window_id] = window_ptr;
    255 }
    256 
    257 void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
    258                                           SessionID::id_type window_id,
    259                                           SessionID::id_type tab_id,
    260                                           size_t tab_index) {
    261   // We're called here for two reasons. 1) We've received an update to the
    262   // SessionWindow information of a SessionHeader node for a foreign session,
    263   // and 2) The SessionHeader node for our local session changed. In both cases
    264   // we need to update our tracking state to reflect the change.
    265   //
    266   // Because the SessionHeader nodes are separate from the individual tab nodes
    267   // and we don't store tab_node_ids in the header / SessionWindow specifics,
    268   // the tab_node_ids are not always available when processing headers.
    269   // We know that we will eventually process (via GetTab) every single tab node
    270   // in the system, so we permit ourselves to use kInvalidTabNodeID here and
    271   // rely on the later update to build the mapping (or a restart).
    272   // TODO(tim): Bug 98892. Update comment when Sync API conversion finishes to
    273   // mention that in the meantime, the only ill effect is that we may not be
    274   // able to fully clean up a stale foreign session, but it will get garbage
    275   // collected eventually.
    276   SessionTab* tab_ptr = GetTabImpl(
    277       session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
    278   unmapped_tabs_.erase(tab_ptr);
    279   synced_tab_map_[session_tag][tab_id].owned = true;
    280   tab_ptr->window_id.set_id(window_id);
    281   DVLOG(1) << "  - tab " << tab_id << " added to window "<< window_id;
    282   DCHECK(GetSession(session_tag)->windows.find(window_id) !=
    283          GetSession(session_tag)->windows.end());
    284   std::vector<SessionTab*>& window_tabs =
    285       GetSession(session_tag)->windows[window_id]->tabs;
    286   if (window_tabs.size() <= tab_index) {
    287     window_tabs.resize(tab_index+1, NULL);
    288   }
    289   DCHECK(!window_tabs[tab_index]);
    290   window_tabs[tab_index] = tab_ptr;
    291 }
    292 
    293 SessionTab* SyncedSessionTracker::GetTab(
    294     const std::string& session_tag,
    295     SessionID::id_type tab_id,
    296     int tab_node_id) {
    297   DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
    298   return GetTabImpl(session_tag, tab_id, tab_node_id);
    299 }
    300 
    301 SessionTab* SyncedSessionTracker::GetTabImpl(
    302     const std::string& session_tag,
    303     SessionID::id_type tab_id,
    304     int tab_node_id) {
    305   SessionTab* tab_ptr = NULL;
    306   IDToSessionTabMap::iterator iter =
    307       synced_tab_map_[session_tag].find(tab_id);
    308   if (iter != synced_tab_map_[session_tag].end()) {
    309     tab_ptr = iter->second.tab_ptr;
    310     if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
    311         tab_id != TabNodePool::kInvalidTabID) {
    312       // We likely created this tab as an out-of-order update to the header
    313       // node for this session before actually associating the tab itself, so
    314       // the tab node id wasn't available at the time.  Update it.
    315 
    316       if (iter->second.tab_node_id != tab_node_id &&
    317           iter->second.tab_node_id != TabNodePool::kInvalidTabNodeID) {
    318         // We are updating tab_node_id for a valid tab_id, ideally this should
    319         // never happen, but there are a few existing foreign sessions that
    320         // may violate this constraint.
    321         // TODO(shashishekhar): Introduce a DCHECK here to enforce this
    322         // constraint in future.
    323         DLOG(ERROR)
    324             << "Updating tab_node_id for " << session_tag << " tab: " << tab_id
    325             << " from: " << iter->second.tab_node_id << " to: " << tab_node_id;
    326       }
    327       iter->second.tab_node_id = tab_node_id;
    328     }
    329 
    330     if (VLOG_IS_ON(1)) {
    331       std::string title;
    332       if (tab_ptr->navigations.size() > 0) {
    333         title = " (" + UTF16ToUTF8(
    334             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
    335       }
    336       DVLOG(1) << "Getting "
    337                << (session_tag == local_session_tag_ ?
    338                    "local session" : session_tag)
    339                << "'s seen tab " << tab_id  << " at " << tab_ptr << title;
    340     }
    341   } else {
    342     tab_ptr = new SessionTab();
    343     tab_ptr->tab_id.set_id(tab_id);
    344     synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
    345                                                              NOT_OWNED,
    346                                                              tab_node_id);
    347     unmapped_tabs_.insert(tab_ptr);
    348     DVLOG(1) << "Getting "
    349              << (session_tag == local_session_tag_ ?
    350                  "local session" : session_tag)
    351              << "'s new tab " << tab_id  << " at " << tab_ptr;
    352   }
    353   DCHECK(tab_ptr);
    354   DCHECK_EQ(tab_ptr->tab_id.id(), tab_id);
    355   return tab_ptr;
    356 }
    357 
    358 void SyncedSessionTracker::Clear() {
    359   // Delete SyncedSession objects (which also deletes all their windows/tabs).
    360   STLDeleteValues(&synced_session_map_);
    361 
    362   // Go through and delete any tabs we had allocated but had not yet placed into
    363   // a SyncedSessionobject.
    364   STLDeleteElements(&unmapped_tabs_);
    365 
    366   // Get rid of our Window/Tab maps (does not delete the actual Window/Tabs
    367   // themselves; they should have all been deleted above).
    368   synced_window_map_.clear();
    369   synced_tab_map_.clear();
    370 
    371   local_session_tag_.clear();
    372 }
    373 
    374 }  // namespace browser_sync
    375