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 "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"
10 namespace browser_sync
{
12 SyncedSessionTracker::SyncedSessionTracker() {
15 SyncedSessionTracker::~SyncedSessionTracker() {
19 void SyncedSessionTracker::SetLocalSessionTag(
20 const std::string
& local_session_tag
) {
21 local_session_tag_
= local_session_tag
;
24 bool SyncedSessionTracker::LookupAllForeignSessions(
25 std::vector
<const SyncedSession
*>* sessions
) const {
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
))) {
44 sessions
->push_back(foreign_session
);
48 return !sessions
->empty();
51 bool SyncedSessionTracker::LookupSessionWindows(
52 const std::string
& session_tag
,
53 std::vector
<const SessionWindow
*>* windows
) const {
56 SyncedSessionMap::const_iterator iter
= synced_session_map_
.find(session_tag
);
57 if (iter
== synced_session_map_
.end())
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
);
68 bool SyncedSessionTracker::LookupSessionTab(
69 const std::string
& tag
,
70 SessionID::id_type tab_id
,
71 const SessionTab
** tab
) const {
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.
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.
86 *tab
= tab_iter
->second
.tab_ptr
;
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())
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
);
107 bool SyncedSessionTracker::LookupLocalSession(const SyncedSession
** output
)
109 SyncedSessionMap::const_iterator it
=
110 synced_session_map_
.find(local_session_tag_
);
111 if (it
!= synced_session_map_
.end()) {
112 *output
= it
->second
;
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
];
125 synced_session
= new SyncedSession
;
126 DVLOG(1) << "Creating new session with tag " << session_tag
<< " at "
128 synced_session
->session_tag
= session_tag
;
129 synced_session_map_
[session_tag
] = synced_session
;
131 DCHECK(synced_session
);
132 return synced_session
;
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;
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;
149 return found_session
;
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();
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;
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
;
193 bool SyncedSessionTracker::DeleteOldSessionTabIfNecessary(
194 SessionTabWrapper tab_wrapper
) {
195 if (!tab_wrapper
.owned
) {
197 SessionTab
* tab_ptr
= tab_wrapper
.tab_ptr
;
199 if (tab_ptr
->navigations
.size() > 0) {
200 title
= " (" + base::UTF16ToUTF8(
201 tab_ptr
->navigations
[tab_ptr
->navigations
.size()-1].title()) + ")";
203 DVLOG(1) << "Deleting closed tab " << tab_ptr
->tab_id
.id() << title
204 << " from window " << tab_ptr
->window_id
.id();
206 unmapped_tabs_
.erase(tab_wrapper
.tab_ptr
);
207 delete tab_wrapper
.tab_ptr
;
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
++);
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
++);
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
);
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
);
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
;
268 void SyncedSessionTracker::PutTabInWindow(const std::string
& session_tag
,
269 SessionID::id_type window_id
,
270 SessionID::id_type tab_id
,
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.
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 unmapped_tabs_
.erase(tab_ptr
);
290 synced_tab_map_
[session_tag
][tab_id
].owned
= true;
291 tab_ptr
->window_id
.set_id(window_id
);
292 DVLOG(1) << " - tab " << tab_id
<< " added to window "<< window_id
;
293 DCHECK(GetSession(session_tag
)->windows
.find(window_id
) !=
294 GetSession(session_tag
)->windows
.end());
295 std::vector
<SessionTab
*>& window_tabs
=
296 GetSession(session_tag
)->windows
[window_id
]->tabs
;
297 if (window_tabs
.size() <= tab_index
) {
298 window_tabs
.resize(tab_index
+1, NULL
);
300 DCHECK(!window_tabs
[tab_index
]);
301 window_tabs
[tab_index
] = tab_ptr
;
304 SessionTab
* SyncedSessionTracker::GetTab(
305 const std::string
& session_tag
,
306 SessionID::id_type tab_id
,
308 DCHECK_NE(TabNodePool::kInvalidTabNodeID
, tab_node_id
);
309 return GetTabImpl(session_tag
, tab_id
, tab_node_id
);
312 SessionTab
* SyncedSessionTracker::GetTabImpl(
313 const std::string
& session_tag
,
314 SessionID::id_type tab_id
,
316 SessionTab
* tab_ptr
= NULL
;
317 IDToSessionTabMap::iterator iter
=
318 synced_tab_map_
[session_tag
].find(tab_id
);
319 if (iter
!= synced_tab_map_
[session_tag
].end()) {
320 tab_ptr
= iter
->second
.tab_ptr
;
321 if (tab_node_id
!= TabNodePool::kInvalidTabNodeID
&&
322 tab_id
!= TabNodePool::kInvalidTabID
) {
323 // We likely created this tab as an out-of-order update to the header
324 // node for this session before actually associating the tab itself, so
325 // the tab node id wasn't available at the time. Update it.
327 if (iter
->second
.tab_node_id
!= tab_node_id
&&
328 iter
->second
.tab_node_id
!= TabNodePool::kInvalidTabNodeID
) {
329 // We are updating tab_node_id for a valid tab_id, ideally this should
330 // never happen, but there are a few existing foreign sessions that
331 // may violate this constraint.
332 // TODO(shashishekhar): Introduce a DCHECK here to enforce this
333 // constraint in future.
335 << "Updating tab_node_id for " << session_tag
<< " tab: " << tab_id
336 << " from: " << iter
->second
.tab_node_id
<< " to: " << tab_node_id
;
338 iter
->second
.tab_node_id
= tab_node_id
;
343 if (tab_ptr
->navigations
.size() > 0) {
344 title
= " (" + base::UTF16ToUTF8(
345 tab_ptr
->navigations
[tab_ptr
->navigations
.size()-1].title()) + ")";
347 DVLOG(1) << "Getting "
348 << (session_tag
== local_session_tag_
?
349 "local session" : session_tag
)
350 << "'s seen tab " << tab_id
<< " at " << tab_ptr
<< title
;
353 tab_ptr
= new SessionTab();
354 tab_ptr
->tab_id
.set_id(tab_id
);
355 synced_tab_map_
[session_tag
][tab_id
] = SessionTabWrapper(tab_ptr
,
358 unmapped_tabs_
.insert(tab_ptr
);
359 DVLOG(1) << "Getting "
360 << (session_tag
== local_session_tag_
?
361 "local session" : session_tag
)
362 << "'s new tab " << tab_id
<< " at " << tab_ptr
;
365 DCHECK_EQ(tab_ptr
->tab_id
.id(), tab_id
);
369 void SyncedSessionTracker::Clear() {
370 // Delete SyncedSession objects (which also deletes all their windows/tabs).
371 STLDeleteValues(&synced_session_map_
);
373 // Go through and delete any tabs we had allocated but had not yet placed into
374 // a SyncedSessionobject.
375 STLDeleteElements(&unmapped_tabs_
);
377 // Get rid of our Window/Tab maps (does not delete the actual Window/Tabs
378 // themselves; they should have all been deleted above).
379 synced_window_map_
.clear();
380 synced_tab_map_
.clear();
382 local_session_tag_
.clear();
385 } // namespace browser_sync