blob: b64fcb16b94df4eb3a93716ff717bd5864506df0 [file] [log] [blame]
[email protected]27684f32012-04-25 18:28:431// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]d70fb362011-06-15 15:07:392// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]35d849dc2011-10-21 23:49:055#include "base/logging.h"
[email protected]420337c2012-10-27 12:25:576#include "base/stl_util.h"
[email protected]e309f312013-06-07 21:50:087#include "base/strings/utf_string_conversions.h"
[email protected]d70fb362011-06-15 15:07:398#include "chrome/browser/sync/glue/synced_session_tracker.h"
[email protected]d70fb362011-06-15 15:07:399
10namespace browser_sync {
11
[email protected]d70fb362011-06-15 15:07:3912SyncedSessionTracker::SyncedSessionTracker() {
13}
14
15SyncedSessionTracker::~SyncedSessionTracker() {
[email protected]13daae6d2011-10-04 13:43:1216 Clear();
[email protected]d70fb362011-06-15 15:07:3917}
18
[email protected]819f753f2011-07-15 21:56:0219void SyncedSessionTracker::SetLocalSessionTag(
20 const std::string& local_session_tag) {
21 local_session_tag_ = local_session_tag;
22}
23
[email protected]d70fb362011-06-15 15:07:3924bool SyncedSessionTracker::LookupAllForeignSessions(
[email protected]13daae6d2011-10-04 13:43:1225 std::vector<const SyncedSession*>* sessions) const {
[email protected]d70fb362011-06-15 15:07:3926 DCHECK(sessions);
[email protected]13daae6d2011-10-04 13:43:1227 sessions->clear();
[email protected]d70fb362011-06-15 15:07:3928 // 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;
[email protected]13daae6d2011-10-04 13:43:1233 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);
[email protected]d70fb362011-06-15 15:07:3945 }
46 }
47
48 return !sessions->empty();
49}
50
51bool SyncedSessionTracker::LookupSessionWindows(
52 const std::string& session_tag,
skuhneb7409dcf2014-11-14 04:06:5553 std::vector<const sessions::SessionWindow*>* windows) const {
[email protected]d70fb362011-06-15 15:07:3954 DCHECK(windows);
[email protected]13daae6d2011-10-04 13:43:1255 windows->clear();
56 SyncedSessionMap::const_iterator iter = synced_session_map_.find(session_tag);
[email protected]d70fb362011-06-15 15:07:3957 if (iter == synced_session_map_.end())
58 return false;
[email protected]13daae6d2011-10-04 13:43:1259 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 }
[email protected]d70fb362011-06-15 15:07:3965 return true;
66}
67
68bool SyncedSessionTracker::LookupSessionTab(
69 const std::string& tag,
70 SessionID::id_type tab_id,
skuhneb7409dcf2014-11-14 04:06:5571 const sessions::SessionTab** tab) const {
[email protected]d70fb362011-06-15 15:07:3972 DCHECK(tab);
[email protected]13daae6d2011-10-04 13:43:1273 SyncedTabMap::const_iterator tab_map_iter = synced_tab_map_.find(tag);
74 if (tab_map_iter == synced_tab_map_.end()) {
[email protected]d70fb362011-06-15 15:07:3975 // We have no record of this session.
76 *tab = NULL;
77 return false;
78 }
[email protected]13daae6d2011-10-04 13:43:1279 IDToSessionTabMap::const_iterator tab_iter =
80 tab_map_iter->second.find(tab_id);
81 if (tab_iter == tab_map_iter->second.end()) {
[email protected]d70fb362011-06-15 15:07:3982 // We have no record of this tab.
83 *tab = NULL;
84 return false;
85 }
[email protected]13daae6d2011-10-04 13:43:1286 *tab = tab_iter->second.tab_ptr;
[email protected]d70fb362011-06-15 15:07:3987 return true;
88}
89
[email protected]98f589d2013-08-01 05:14:0190bool 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
[email protected]85622cd2013-12-18 17:32:27107bool 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
[email protected]d70fb362011-06-15 15:07:39118SyncedSession* SyncedSessionTracker::GetSession(
119 const std::string& session_tag) {
[email protected]13daae6d2011-10-04 13:43:12120 SyncedSession* synced_session = NULL;
[email protected]d70fb362011-06-15 15:07:39121 if (synced_session_map_.find(session_tag) !=
122 synced_session_map_.end()) {
[email protected]13daae6d2011-10-04 13:43:12123 synced_session = synced_session_map_[session_tag];
[email protected]d70fb362011-06-15 15:07:39124 } else {
[email protected]13daae6d2011-10-04 13:43:12125 synced_session = new SyncedSession;
[email protected]060588c2011-11-29 21:38:41126 DVLOG(1) << "Creating new session with tag " << session_tag << " at "
127 << synced_session;
[email protected]d70fb362011-06-15 15:07:39128 synced_session->session_tag = session_tag;
[email protected]13daae6d2011-10-04 13:43:12129 synced_session_map_[session_tag] = synced_session;
[email protected]d70fb362011-06-15 15:07:39130 }
[email protected]13daae6d2011-10-04 13:43:12131 DCHECK(synced_session);
132 return synced_session;
[email protected]d70fb362011-06-15 15:07:39133}
134
[email protected]16499062011-10-11 07:02:40135bool SyncedSessionTracker::DeleteSession(const std::string& session_tag) {
136 bool found_session = false;
137 SyncedSessionMap::iterator iter = synced_session_map_.find(session_tag);
[email protected]d70fb362011-06-15 15:07:39138 if (iter != synced_session_map_.end()) {
[email protected]16499062011-10-11 07:02:40139 SyncedSession* session = iter->second;
[email protected]d70fb362011-06-15 15:07:39140 synced_session_map_.erase(iter);
[email protected]16499062011-10-11 07:02:40141 delete session; // Delete the SyncedSession object.
142 found_session = true;
[email protected]d70fb362011-06-15 15:07:39143 }
[email protected]16499062011-10-11 07:02:40144 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;
[email protected]d70fb362011-06-15 15:07:39150}
151
[email protected]13daae6d2011-10-04 13:43:12152void 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();
[email protected]819f753f2011-07-15 21:56:02165 }
[email protected]13daae6d2011-10-04 13:43:12166 }
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
179bool SyncedSessionTracker::DeleteOldSessionWindowIfNecessary(
180 SessionWindowWrapper window_wrapper) {
[email protected]420337c2012-10-27 12:25:57181 // Clear the tabs first, since we don't want the destructor to destroy
182 // them. Their deletion will be handled by DeleteOldSessionTab below.
[email protected]13daae6d2011-10-04 13:43:12183 if (!window_wrapper.owned) {
[email protected]060588c2011-11-29 21:38:41184 DVLOG(1) << "Deleting closed window "
185 << window_wrapper.window_ptr->window_id.id();
[email protected]13daae6d2011-10-04 13:43:12186 window_wrapper.window_ptr->tabs.clear();
187 delete window_wrapper.window_ptr;
188 return true;
189 }
190 return false;
191}
192
193bool SyncedSessionTracker::DeleteOldSessionTabIfNecessary(
194 SessionTabWrapper tab_wrapper) {
195 if (!tab_wrapper.owned) {
196 if (VLOG_IS_ON(1)) {
skuhneb7409dcf2014-11-14 04:06:55197 sessions::SessionTab* tab_ptr = tab_wrapper.tab_ptr;
[email protected]35d849dc2011-10-21 23:49:05198 std::string title;
[email protected]13daae6d2011-10-04 13:43:12199 if (tab_ptr->navigations.size() > 0) {
[email protected]f911df52013-12-24 23:24:23200 title = " (" + base::UTF16ToUTF8(
[email protected]13daae6d2011-10-04 13:43:12201 tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
202 }
[email protected]060588c2011-11-29 21:38:41203 DVLOG(1) << "Deleting closed tab " << tab_ptr->tab_id.id() << title
204 << " from window " << tab_ptr->window_id.id();
[email protected]13daae6d2011-10-04 13:43:12205 }
206 unmapped_tabs_.erase(tab_wrapper.tab_ptr);
207 delete tab_wrapper.tab_ptr;
208 return true;
209 }
210 return false;
211}
212
213void 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
240void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
241 SessionID::id_type window_id) {
skuhneb7409dcf2014-11-14 04:06:55242 sessions::SessionWindow* window_ptr = NULL;
[email protected]13daae6d2011-10-04 13:43:12243 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;
[email protected]060588c2011-11-29 21:38:41248 DVLOG(1) << "Putting seen window " << window_id << " at " << window_ptr
249 << "in " << (session_tag == local_session_tag_ ?
250 "local session" : session_tag);
[email protected]d70fb362011-06-15 15:07:39251 } else {
[email protected]13daae6d2011-10-04 13:43:12252 // Create the window.
skuhneb7409dcf2014-11-14 04:06:55253 window_ptr = new sessions::SessionWindow();
[email protected]13daae6d2011-10-04 13:43:12254 window_ptr->window_id.set_id(window_id);
255 synced_window_map_[session_tag][window_id] =
[email protected]98f589d2013-08-01 05:14:01256 SessionWindowWrapper(window_ptr, IS_OWNED);
[email protected]060588c2011-11-29 21:38:41257 DVLOG(1) << "Putting new window " << window_id << " at " << window_ptr
258 << "in " << (session_tag == local_session_tag_ ?
259 "local session" : session_tag);
[email protected]13daae6d2011-10-04 13:43:12260 }
261 DCHECK(window_ptr);
262 DCHECK_EQ(window_ptr->window_id.id(), window_id);
skuhneb7409dcf2014-11-14 04:06:55263 DCHECK_EQ(reinterpret_cast<sessions::SessionWindow*>(NULL),
[email protected]420337c2012-10-27 12:25:57264 GetSession(session_tag)->windows[window_id]);
[email protected]13daae6d2011-10-04 13:43:12265 GetSession(session_tag)->windows[window_id] = window_ptr;
266}
267
268void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
269 SessionID::id_type window_id,
270 SessionID::id_type tab_id,
271 size_t tab_index) {
[email protected]98f589d2013-08-01 05:14:01272 // 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.
skuhneb7409dcf2014-11-14 04:06:55287 sessions::SessionTab* tab_ptr = GetTabImpl(
[email protected]98f589d2013-08-01 05:14:01288 session_tag, tab_id, TabNodePool::kInvalidTabNodeID);
[email protected]abb35c12014-08-21 20:13:44289
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
[email protected]13daae6d2011-10-04 13:43:12300 unmapped_tabs_.erase(tab_ptr);
301 synced_tab_map_[session_tag][tab_id].owned = true;
[email protected]abb35c12014-08-21 20:13:44302
[email protected]13daae6d2011-10-04 13:43:12303 tab_ptr->window_id.set_id(window_id);
[email protected]060588c2011-11-29 21:38:41304 DVLOG(1) << " - tab " << tab_id << " added to window "<< window_id;
[email protected]13daae6d2011-10-04 13:43:12305 DCHECK(GetSession(session_tag)->windows.find(window_id) !=
306 GetSession(session_tag)->windows.end());
skuhneb7409dcf2014-11-14 04:06:55307 std::vector<sessions::SessionTab*>& window_tabs =
[email protected]13daae6d2011-10-04 13:43:12308 GetSession(session_tag)->windows[window_id]->tabs;
309 if (window_tabs.size() <= tab_index) {
310 window_tabs.resize(tab_index+1, NULL);
311 }
[email protected]9dd1d582012-09-19 04:04:16312 DCHECK(!window_tabs[tab_index]);
[email protected]13daae6d2011-10-04 13:43:12313 window_tabs[tab_index] = tab_ptr;
314}
315
skuhneb7409dcf2014-11-14 04:06:55316sessions::SessionTab* SyncedSessionTracker::GetTab(
[email protected]13daae6d2011-10-04 13:43:12317 const std::string& session_tag,
[email protected]98f589d2013-08-01 05:14:01318 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
skuhneb7409dcf2014-11-14 04:06:55324sessions::SessionTab* SyncedSessionTracker::GetTabImpl(
[email protected]98f589d2013-08-01 05:14:01325 const std::string& session_tag,
326 SessionID::id_type tab_id,
327 int tab_node_id) {
skuhneb7409dcf2014-11-14 04:06:55328 sessions::SessionTab* tab_ptr = NULL;
[email protected]13daae6d2011-10-04 13:43:12329 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;
[email protected]98f589d2013-08-01 05:14:01333 if (tab_node_id != TabNodePool::kInvalidTabNodeID &&
334 tab_id != TabNodePool::kInvalidTabID) {
[email protected]c7a73c992014-03-05 10:00:40335 // 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.
[email protected]98f589d2013-08-01 05:14:01355 iter->second.tab_node_id = tab_node_id;
356 }
357
[email protected]35d849dc2011-10-21 23:49:05358 if (VLOG_IS_ON(1)) {
359 std::string title;
360 if (tab_ptr->navigations.size() > 0) {
[email protected]f911df52013-12-24 23:24:23361 title = " (" + base::UTF16ToUTF8(
[email protected]35d849dc2011-10-21 23:49:05362 tab_ptr->navigations[tab_ptr->navigations.size()-1].title()) + ")";
363 }
[email protected]060588c2011-11-29 21:38:41364 DVLOG(1) << "Getting "
365 << (session_tag == local_session_tag_ ?
366 "local session" : session_tag)
367 << "'s seen tab " << tab_id << " at " << tab_ptr << title;
[email protected]13daae6d2011-10-04 13:43:12368 }
[email protected]13daae6d2011-10-04 13:43:12369 } else {
skuhneb7409dcf2014-11-14 04:06:55370 tab_ptr = new sessions::SessionTab();
[email protected]13daae6d2011-10-04 13:43:12371 tab_ptr->tab_id.set_id(tab_id);
[email protected]98f589d2013-08-01 05:14:01372 synced_tab_map_[session_tag][tab_id] = SessionTabWrapper(tab_ptr,
373 NOT_OWNED,
374 tab_node_id);
[email protected]13daae6d2011-10-04 13:43:12375 unmapped_tabs_.insert(tab_ptr);
[email protected]060588c2011-11-29 21:38:41376 DVLOG(1) << "Getting "
377 << (session_tag == local_session_tag_ ?
378 "local session" : session_tag)
379 << "'s new tab " << tab_id << " at " << tab_ptr;
[email protected]d70fb362011-06-15 15:07:39380 }
[email protected]13daae6d2011-10-04 13:43:12381 DCHECK(tab_ptr);
382 DCHECK_EQ(tab_ptr->tab_id.id(), tab_id);
383 return tab_ptr;
[email protected]d70fb362011-06-15 15:07:39384}
385
[email protected]13daae6d2011-10-04 13:43:12386void SyncedSessionTracker::Clear() {
[email protected]d70fb362011-06-15 15:07:39387 // Delete SyncedSession objects (which also deletes all their windows/tabs).
[email protected]420337c2012-10-27 12:25:57388 STLDeleteValues(&synced_session_map_);
[email protected]d70fb362011-06-15 15:07:39389
[email protected]d70fb362011-06-15 15:07:39390 // Go through and delete any tabs we had allocated but had not yet placed into
391 // a SyncedSessionobject.
[email protected]420337c2012-10-27 12:25:57392 STLDeleteElements(&unmapped_tabs_);
[email protected]819f753f2011-07-15 21:56:02393
[email protected]13daae6d2011-10-04 13:43:12394 // 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
[email protected]819f753f2011-07-15 21:56:02399 local_session_tag_.clear();
[email protected]d70fb362011-06-15 15:07:39400}
401
402} // namespace browser_sync