• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SyncedSessionTracker()12 SyncedSessionTracker::SyncedSessionTracker() {
13 }
14 
~SyncedSessionTracker()15 SyncedSessionTracker::~SyncedSessionTracker() {
16   Clear();
17 }
18 
SetLocalSessionTag(const std::string & local_session_tag)19 void SyncedSessionTracker::SetLocalSessionTag(
20     const std::string& local_session_tag) {
21   local_session_tag_ = local_session_tag;
22 }
23 
LookupAllForeignSessions(std::vector<const SyncedSession * > * sessions) const24 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 
LookupSessionWindows(const std::string & session_tag,std::vector<const SessionWindow * > * windows) const51 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 
LookupSessionTab(const std::string & tag,SessionID::id_type tab_id,const SessionTab ** tab) const68 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 
LookupTabNodeIds(const std::string & session_tag,std::set<int> * tab_node_ids)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 
LookupLocalSession(const SyncedSession ** output) const107 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 
GetSession(const std::string & session_tag)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 
DeleteSession(const std::string & session_tag)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 
ResetSessionTracking(const std::string & session_tag)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 
DeleteOldSessionWindowIfNecessary(SessionWindowWrapper window_wrapper)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 
DeleteOldSessionTabIfNecessary(SessionTabWrapper tab_wrapper)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 
CleanupSession(const std::string & session_tag)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 
PutWindowInSession(const std::string & session_tag,SessionID::id_type window_id)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 
PutTabInWindow(const std::string & session_tag,SessionID::id_type window_id,SessionID::id_type tab_id,size_t tab_index)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 
290   // It's up to the caller to ensure this never happens.  Tabs should not
291   // belong to more than one window or appear twice within the same window.
292   //
293   // If this condition were violated, we would double-free during shutdown.
294   // That could cause all sorts of hard to diagnose crashes, possibly in code
295   // far away from here.  We crash early to avoid this.
296   //
297   // See http://crbug.com/360822.
298   CHECK(!synced_tab_map_[session_tag][tab_id].owned);
299 
300   unmapped_tabs_.erase(tab_ptr);
301   synced_tab_map_[session_tag][tab_id].owned = true;
302 
303   tab_ptr->window_id.set_id(window_id);
304   DVLOG(1) << "  - tab " << tab_id << " added to window "<< window_id;
305   DCHECK(GetSession(session_tag)->windows.find(window_id) !=
306          GetSession(session_tag)->windows.end());
307   std::vector<SessionTab*>& window_tabs =
308       GetSession(session_tag)->windows[window_id]->tabs;
309   if (window_tabs.size() <= tab_index) {
310     window_tabs.resize(tab_index+1, NULL);
311   }
312   DCHECK(!window_tabs[tab_index]);
313   window_tabs[tab_index] = tab_ptr;
314 }
315 
GetTab(const std::string & session_tag,SessionID::id_type tab_id,int tab_node_id)316 SessionTab* SyncedSessionTracker::GetTab(
317     const std::string& session_tag,
318     SessionID::id_type tab_id,
319     int tab_node_id) {
320   DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
321   return GetTabImpl(session_tag, tab_id, tab_node_id);
322 }
323 
GetTabImpl(const std::string & session_tag,SessionID::id_type tab_id,int tab_node_id)324 SessionTab* SyncedSessionTracker::GetTabImpl(
325     const std::string& session_tag,
326     SessionID::id_type tab_id,
327     int tab_node_id) {
328   SessionTab* tab_ptr = NULL;
329   IDToSessionTabMap::iterator iter =
330       synced_tab_map_[session_tag].find(tab_id);
331   if (iter != synced_tab_map_[session_tag].end()) {
332     tab_ptr = iter->second.tab_ptr;
333     if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
334         tab_id != TabNodePool::kInvalidTabID) {
335       // TabIDs are not stable across restarts of a client. Consider this
336       // example with two tabs:
337       //
338       // http://a.com  TabID1 --> NodeIDA
339       // http://b.com  TabID2 --> NodeIDB
340       //
341       // After restart, tab ids are reallocated. e.g, one possibility:
342       // http://a.com TabID2 --> NodeIDA
343       // http://b.com TabID1 --> NodeIDB
344       //
345       // If that happend on a remote client, here we will see an update to
346       // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
347       // with tab_node_id changing from NodeIDB to NodeIDA.
348       //
349       // We can also wind up here if we created this tab as an out-of-order
350       // update to the header node for this session before actually associating
351       // the tab itself, so the tab node id wasn't available at the time and
352       // is currenlty kInvalidTabNodeID.
353       //
354       // In both cases, we update the tab_node_id.
355       iter->second.tab_node_id = tab_node_id;
356     }
357 
358     if (VLOG_IS_ON(1)) {
359       std::string title;
360       if (tab_ptr->navigations.size() > 0) {
361         title = " (" + base::UTF16ToUTF8(
362             tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
363       }
364       DVLOG(1) << "Getting "
365                << (session_tag == local_session_tag_ ?
366                    "local session" : session_tag)
367                << "'s seen tab " << tab_id  << " at " << tab_ptr << title;
368     }
369   } else {
370     tab_ptr = new SessionTab();
371     tab_ptr->tab_id.set_id(tab_id);
372     synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
373                                                              NOT_OWNED,
374                                                              tab_node_id);
375     unmapped_tabs_.insert(tab_ptr);
376     DVLOG(1) << "Getting "
377              << (session_tag == local_session_tag_ ?
378                  "local session" : session_tag)
379              << "'s new tab " << tab_id  << " at " << tab_ptr;
380   }
381   DCHECK(tab_ptr);
382   DCHECK_EQ(tab_ptr->tab_id.id(), tab_id);
383   return tab_ptr;
384 }
385 
Clear()386 void SyncedSessionTracker::Clear() {
387   // Delete SyncedSession objects (which also deletes all their windows/tabs).
388   STLDeleteValues(&synced_session_map_);
389 
390   // Go through and delete any tabs we had allocated but had not yet placed into
391   // a SyncedSessionobject.
392   STLDeleteElements(&unmapped_tabs_);
393 
394   // Get rid of our Window/Tab maps (does not delete the actual Window/Tabs
395   // themselves; they should have all been deleted above).
396   synced_window_map_.clear();
397   synced_tab_map_.clear();
398 
399   local_session_tag_.clear();
400 }
401 
402 }  // namespace browser_sync
403