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