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