blob: dead372fccfdc2a8b0e4462b0b360fd54e01f66c [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
zeac2f678e2015-10-28 06:16:105#include "components/sync_sessions/synced_session_tracker.h"
droger6a118632015-06-23 17:59:456
Mikel Astizf82416e902018-02-27 16:11:137#include <algorithm>
skym6d5ecf82016-10-04 17:43:458#include <utility>
9
[email protected]35d849dc2011-10-21 23:49:0510#include "base/logging.h"
[email protected]e309f312013-06-07 21:50:0811#include "base/strings/utf_string_conversions.h"
Mikel Astizf82416e902018-02-27 16:11:1312#include "components/sync/protocol/session_specifics.pb.h"
zeaf09345c2015-10-27 05:29:5013#include "components/sync_sessions/sync_sessions_client.h"
pnoland1901afa52017-03-23 21:24:0014#include "components/sync_sessions/synced_tab_delegate.h"
Mikel Astizf82416e902018-02-27 16:11:1315
maxboguea79d99b72016-09-15 15:59:1616namespace sync_sessions {
[email protected]d70fb362011-06-15 15:07:3917
zeaf09345c2015-10-27 05:29:5018namespace {
19
20// Helper for iterating through all tabs within a window, and all navigations
21// within a tab, to find if there's a valid syncable url.
maxboguea79d99b72016-09-15 15:59:1622bool ShouldSyncSessionWindow(SyncSessionsClient* sessions_client,
zeaf09345c2015-10-27 05:29:5023 const sessions::SessionWindow& window) {
avi498cabca2016-09-21 19:03:5024 for (const auto& tab : window.tabs) {
zeaf09345c2015-10-27 05:29:5025 for (const sessions::SerializedNavigationEntry& navigation :
26 tab->navigations) {
27 if (sessions_client->ShouldSyncURL(navigation.virtual_url())) {
28 return true;
29 }
30 }
31 }
32 return false;
[email protected]d70fb362011-06-15 15:07:3933}
34
skym199c7fd2016-04-14 19:26:4235// Presentable means |foreign_session| must have syncable content.
maxboguea79d99b72016-09-15 15:59:1636bool IsPresentable(SyncSessionsClient* sessions_client,
Mikel Astiz6d052892018-03-29 07:15:5537 const SyncedSession& foreign_session) {
38 for (auto iter = foreign_session.windows.begin();
39 iter != foreign_session.windows.end(); ++iter) {
zea325508e2017-03-29 20:11:2240 if (ShouldSyncSessionWindow(sessions_client,
41 iter->second->wrapped_window)) {
skym199c7fd2016-04-14 19:26:4242 return true;
43 }
44 }
45 return false;
46}
47
Mikel Astizf82416e902018-02-27 16:11:1348// Verify that tab IDs appear only once within a session. Intended to prevent
49// http://crbug.com/360822.
50bool IsValidSessionHeader(const sync_pb::SessionHeader& header) {
51 std::set<int> session_tab_ids;
52 for (int i = 0; i < header.window_size(); ++i) {
53 const sync_pb::SessionWindow& window = header.window(i);
54 for (int j = 0; j < window.tab_size(); ++j) {
55 const int tab_id = window.tab(j);
56 bool success = session_tab_ids.insert(tab_id).second;
57 if (!success)
58 return false;
59 }
60 }
61
62 return true;
63}
64
65void PopulateSyncedSessionWindowFromSpecifics(
66 const std::string& session_tag,
67 const sync_pb::SessionWindow& specifics,
68 base::Time mtime,
69 SyncedSessionWindow* synced_session_window,
70 SyncedSessionTracker* tracker) {
71 sessions::SessionWindow* session_window =
72 &synced_session_window->wrapped_window;
Mikel Astize023c572018-03-28 07:56:5673 if (specifics.has_window_id()) {
74 session_window->window_id =
75 SessionID::FromSerializedValue(specifics.window_id());
76 }
Mikel Astizf82416e902018-02-27 16:11:1377 if (specifics.has_selected_tab_index())
78 session_window->selected_tab_index = specifics.selected_tab_index();
79 synced_session_window->window_type = specifics.browser_type();
80 if (specifics.has_browser_type()) {
81 if (specifics.browser_type() ==
82 sync_pb::SessionWindow_BrowserType_TYPE_TABBED) {
83 session_window->type = sessions::SessionWindow::TYPE_TABBED;
84 } else {
85 // Note: custom tabs are treated like popup windows on restore, as you can
86 // restore a custom tab on a platform that doesn't support them.
87 session_window->type = sessions::SessionWindow::TYPE_POPUP;
88 }
89 }
90 session_window->timestamp = mtime;
91 session_window->tabs.clear();
92 for (int i = 0; i < specifics.tab_size(); i++) {
Mikel Astize023c572018-03-28 07:56:5693 SessionID tab_id = SessionID::FromSerializedValue(specifics.tab(i));
94 tracker->PutTabInWindow(session_tag, session_window->window_id, tab_id);
Mikel Astizf82416e902018-02-27 16:11:1395 }
96}
97
98void PopulateSyncedSessionFromSpecifics(
99 const std::string& session_tag,
100 const sync_pb::SessionHeader& header_specifics,
101 base::Time mtime,
102 SyncedSession* synced_session,
103 SyncedSessionTracker* tracker) {
104 if (header_specifics.has_client_name())
105 synced_session->session_name = header_specifics.client_name();
106 if (header_specifics.has_device_type()) {
107 synced_session->device_type = header_specifics.device_type();
108 }
109 synced_session->modified_time =
110 std::max(mtime, synced_session->modified_time);
111
112 // Process all the windows and their tab information.
113 int num_windows = header_specifics.window_size();
114 DVLOG(1) << "Populating " << session_tag << " with " << num_windows
115 << " windows.";
116
117 for (int i = 0; i < num_windows; ++i) {
118 const sync_pb::SessionWindow& window_s = header_specifics.window(i);
Mikel Astize023c572018-03-28 07:56:56119 SessionID window_id = SessionID::FromSerializedValue(window_s.window_id());
Mikel Astizf82416e902018-02-27 16:11:13120 tracker->PutWindowInSession(session_tag, window_id);
121 PopulateSyncedSessionWindowFromSpecifics(
122 session_tag, window_s, synced_session->modified_time,
123 synced_session->windows[window_id].get(), tracker);
124 }
125}
126
zeaf09345c2015-10-27 05:29:50127} // namespace
128
Mikel Astiz6d052892018-03-29 07:15:55129SyncedSessionTracker::TrackedSession::TrackedSession() {}
130
131SyncedSessionTracker::TrackedSession::~TrackedSession() {}
132
maxboguea79d99b72016-09-15 15:59:16133SyncedSessionTracker::SyncedSessionTracker(SyncSessionsClient* sessions_client)
zeaf09345c2015-10-27 05:29:50134 : sessions_client_(sessions_client) {}
135
Mikel Astiz6d052892018-03-29 07:15:55136SyncedSessionTracker::~SyncedSessionTracker() {}
[email protected]d70fb362011-06-15 15:07:39137
Mikel Astiz556bf092018-03-06 13:16:33138void SyncedSessionTracker::InitLocalSession(
139 const std::string& local_session_tag,
140 const std::string& local_session_name,
141 sync_pb::SyncEnums::DeviceType local_device_type) {
zea9b42f4312017-03-01 19:15:44142 DCHECK(local_session_tag_.empty());
143 DCHECK(!local_session_tag.empty());
[email protected]819f753f2011-07-15 21:56:02144 local_session_tag_ = local_session_tag;
Mikel Astiz556bf092018-03-06 13:16:33145
146 SyncedSession* local_session = GetSession(local_session_tag);
147 local_session->session_name = local_session_name;
148 local_session->device_type = local_device_type;
149 local_session->session_tag = local_session_tag;
150}
151
152const std::string& SyncedSessionTracker::GetLocalSessionTag() const {
153 return local_session_tag_;
[email protected]819f753f2011-07-15 21:56:02154}
155
Mikel Astiz6d052892018-03-29 07:15:55156std::vector<const SyncedSession*>
157SyncedSessionTracker::LookupAllForeignSessions(SessionLookup lookup) const {
158 std::vector<const SyncedSession*> sessions;
159 for (const auto& session_pair : session_map_) {
160 const SyncedSession& foreign_session = session_pair.second.synced_session;
avi498cabca2016-09-21 19:03:50161 if (session_pair.first != local_session_tag_ &&
skym199c7fd2016-04-14 19:26:42162 (lookup == RAW || IsPresentable(sessions_client_, foreign_session))) {
Mikel Astiz6d052892018-03-29 07:15:55163 sessions.push_back(&foreign_session);
[email protected]d70fb362011-06-15 15:07:39164 }
165 }
Mikel Astiz6d052892018-03-29 07:15:55166 return sessions;
[email protected]d70fb362011-06-15 15:07:39167}
168
169bool SyncedSessionTracker::LookupSessionWindows(
170 const std::string& session_tag,
skuhneb7409dcf2014-11-14 04:06:55171 std::vector<const sessions::SessionWindow*>* windows) const {
[email protected]d70fb362011-06-15 15:07:39172 DCHECK(windows);
[email protected]13daae6d2011-10-04 13:43:12173 windows->clear();
avi498cabca2016-09-21 19:03:50174
Mikel Astiz6d052892018-03-29 07:15:55175 const TrackedSession* session = LookupTrackedSession(session_tag);
176 if (!session)
177 return false; // We have no record of this session.
178
179 for (const auto& window_pair : session->synced_session.windows)
zea325508e2017-03-29 20:11:22180 windows->push_back(&window_pair.second->wrapped_window);
avi498cabca2016-09-21 19:03:50181
[email protected]d70fb362011-06-15 15:07:39182 return true;
183}
184
Mikel Astiz6d052892018-03-29 07:15:55185const sessions::SessionTab* SyncedSessionTracker::LookupSessionTab(
[email protected]d70fb362011-06-15 15:07:39186 const std::string& tag,
Mikel Astiz6d052892018-03-29 07:15:55187 SessionID tab_id) const {
Mikel Astize023c572018-03-28 07:56:56188 if (!tab_id.is_valid())
Mikel Astiz6d052892018-03-29 07:15:55189 return nullptr;
zea9b42f4312017-03-01 19:15:44190
Mikel Astiz6d052892018-03-29 07:15:55191 const TrackedSession* session = LookupTrackedSession(tag);
192 if (!session)
193 return nullptr; // We have no record of this session.
194
195 auto tab_iter = session->synced_tab_map.find(tab_id);
196 if (tab_iter == session->synced_tab_map.end())
197 return nullptr; // We have no record of this tab.
198
199 return tab_iter->second;
[email protected]d70fb362011-06-15 15:07:39200}
201
zea9b42f4312017-03-01 19:15:44202void SyncedSessionTracker::LookupForeignTabNodeIds(
203 const std::string& session_tag,
204 std::set<int>* tab_node_ids) const {
[email protected]98f589d2013-08-01 05:14:01205 tab_node_ids->clear();
Mikel Astiz6d052892018-03-29 07:15:55206 const TrackedSession* session = LookupTrackedSession(session_tag);
207 if (session) {
208 tab_node_ids->insert(session->tab_node_ids.begin(),
209 session->tab_node_ids.end());
[email protected]98f589d2013-08-01 05:14:01210 }
avi498cabca2016-09-21 19:03:50211 // In case an invalid node id was included, remove it.
skym199c7fd2016-04-14 19:26:42212 tab_node_ids->erase(TabNodePool::kInvalidTabNodeID);
[email protected]98f589d2013-08-01 05:14:01213}
214
Mikel Astiz6d052892018-03-29 07:15:55215const SyncedSession* SyncedSessionTracker::LookupLocalSession() const {
216 const TrackedSession* session = LookupTrackedSession(local_session_tag_);
217 return session ? &session->synced_session : nullptr;
[email protected]85622cd2013-12-18 17:32:27218}
219
maxboguea79d99b72016-09-15 15:59:16220SyncedSession* SyncedSessionTracker::GetSession(
[email protected]d70fb362011-06-15 15:07:39221 const std::string& session_tag) {
Mikel Astiz6d052892018-03-29 07:15:55222 return &GetTrackedSession(session_tag)->synced_session;
[email protected]d70fb362011-06-15 15:07:39223}
224
zea9b42f4312017-03-01 19:15:44225bool SyncedSessionTracker::DeleteForeignSession(
226 const std::string& session_tag) {
227 DCHECK_NE(local_session_tag_, session_tag);
skym199c7fd2016-04-14 19:26:42228
Mikel Astiz6d052892018-03-29 07:15:55229 auto iter = session_map_.find(session_tag);
230 if (iter == session_map_.end())
231 return false;
skym199c7fd2016-04-14 19:26:42232
Mikel Astiz6d052892018-03-29 07:15:55233 // An implicitly created session that has children tabs but no header node
234 // will have never had the device_type changed from unset.
235 const bool header_existed =
236 iter->second.synced_session.device_type != sync_pb::SyncEnums::TYPE_UNSET;
237 // SyncedSession's destructor will trigger deletion of windows which will in
238 // turn trigger the deletion of tabs. This doesn't affect the convenience
239 // maps.
240 session_map_.erase(iter);
skym199c7fd2016-04-14 19:26:42241
242 return header_existed;
[email protected]d70fb362011-06-15 15:07:39243}
244
[email protected]13daae6d2011-10-04 13:43:12245void SyncedSessionTracker::ResetSessionTracking(
246 const std::string& session_tag) {
Mikel Astiz6d052892018-03-29 07:15:55247 TrackedSession* session = GetTrackedSession(session_tag);
[email protected]13daae6d2011-10-04 13:43:12248
Mikel Astiz6d052892018-03-29 07:15:55249 for (auto& window_pair : session->synced_session.windows) {
avi498cabca2016-09-21 19:03:50250 // First unmap the tabs in the window.
zea325508e2017-03-29 20:11:22251 for (auto& tab : window_pair.second->wrapped_window.tabs) {
Mikel Astize023c572018-03-28 07:56:56252 SessionID tab_id = tab->tab_id;
Mikel Astiz6d052892018-03-29 07:15:55253 session->unmapped_tabs[tab_id] = std::move(tab);
[email protected]13daae6d2011-10-04 13:43:12254 }
zea325508e2017-03-29 20:11:22255 window_pair.second->wrapped_window.tabs.clear();
avi498cabca2016-09-21 19:03:50256
257 // Then unmap the window itself.
Mikel Astiz6d052892018-03-29 07:15:55258 session->unmapped_windows[window_pair.first] =
avi498cabca2016-09-21 19:03:50259 std::move(window_pair.second);
[email protected]13daae6d2011-10-04 13:43:12260 }
Mikel Astiz6d052892018-03-29 07:15:55261 session->synced_session.windows.clear();
[email protected]13daae6d2011-10-04 13:43:12262}
263
skym199c7fd2016-04-14 19:26:42264void SyncedSessionTracker::DeleteForeignTab(const std::string& session_tag,
265 int tab_node_id) {
zea9b42f4312017-03-01 19:15:44266 DCHECK_NE(local_session_tag_, session_tag);
Mikel Astiz6d052892018-03-29 07:15:55267 TrackedSession* session = LookupTrackedSession(session_tag);
268 if (session)
269 session->tab_node_ids.erase(tab_node_id);
270}
271
272const SyncedSessionTracker::TrackedSession*
273SyncedSessionTracker::LookupTrackedSession(
274 const std::string& session_tag) const {
275 std::map<std::string, TrackedSession>::const_iterator it =
276 session_map_.find(session_tag);
277 return it == session_map_.end() ? nullptr : &it->second;
278}
279
280SyncedSessionTracker::TrackedSession*
281SyncedSessionTracker::LookupTrackedSession(const std::string& session_tag) {
282 std::map<std::string, TrackedSession>::iterator it =
283 session_map_.find(session_tag);
284 return it == session_map_.end() ? nullptr : &it->second;
285}
286
287SyncedSessionTracker::TrackedSession* SyncedSessionTracker::GetTrackedSession(
288 const std::string& session_tag) {
289 TrackedSession* session = LookupTrackedSession(session_tag);
290 if (session)
291 return session;
292
293 session = &session_map_[session_tag];
294 DVLOG(1) << "Creating new session with tag " << session_tag << " at "
295 << session;
296 session->synced_session.session_tag = session_tag;
297 return session;
skym199c7fd2016-04-14 19:26:42298}
299
zea9b42f4312017-03-01 19:15:44300void SyncedSessionTracker::CleanupSessionImpl(const std::string& session_tag) {
Mikel Astiz6d052892018-03-29 07:15:55301 TrackedSession* session = LookupTrackedSession(session_tag);
302 if (!session)
303 return;
[email protected]13daae6d2011-10-04 13:43:12304
Mikel Astiz6d052892018-03-29 07:15:55305 for (const auto& window_pair : session->unmapped_windows)
306 session->synced_window_map.erase(window_pair.first);
307 session->unmapped_windows.clear();
308
309 for (const auto& tab_pair : session->unmapped_tabs)
310 session->synced_tab_map.erase(tab_pair.first);
311 session->unmapped_tabs.clear();
[email protected]13daae6d2011-10-04 13:43:12312}
313
Mikel Astize023c572018-03-28 07:56:56314bool SyncedSessionTracker::IsTabUnmappedForTesting(SessionID tab_id) {
Mikel Astiz6d052892018-03-29 07:15:55315 const TrackedSession* session = LookupTrackedSession(local_session_tag_);
316 if (!session)
317 return false;
318
319 return session->unmapped_tabs.count(tab_id) != 0;
320}
321
322std::set<int> SyncedSessionTracker::GetTabNodeIdsForTesting(
323 const std::string& session_tag) const {
324 const TrackedSession* session = LookupTrackedSession(session_tag);
325 return session ? session->tab_node_ids : std::set<int>();
zea9b42f4312017-03-01 19:15:44326}
327
[email protected]13daae6d2011-10-04 13:43:12328void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
Mikel Astize023c572018-03-28 07:56:56329 SessionID window_id) {
Mikel Astiz6d052892018-03-29 07:15:55330 TrackedSession* session = GetTrackedSession(session_tag);
331 if (session->synced_session.windows.count(window_id) != 0) {
zea92d4a102017-04-18 06:26:07332 DVLOG(1) << "Window " << window_id << " already added to session "
333 << session_tag;
334 return;
335 }
zea325508e2017-03-29 20:11:22336 std::unique_ptr<SyncedSessionWindow> window;
avi498cabca2016-09-21 19:03:50337
Mikel Astiz6d052892018-03-29 07:15:55338 auto iter = session->unmapped_windows.find(window_id);
339 if (iter != session->unmapped_windows.end()) {
340 DCHECK_EQ(session->synced_window_map[window_id], iter->second.get());
avi498cabca2016-09-21 19:03:50341 window = std::move(iter->second);
Mikel Astiz6d052892018-03-29 07:15:55342 session->unmapped_windows.erase(iter);
avi498cabca2016-09-21 19:03:50343 DVLOG(1) << "Putting seen window " << window_id << " at " << window.get()
zeac2f678e2015-10-28 06:16:10344 << "in " << (session_tag == local_session_tag_ ? "local session"
345 : session_tag);
[email protected]d70fb362011-06-15 15:07:39346 } else {
[email protected]13daae6d2011-10-04 13:43:12347 // Create the window.
Sky Malice08a04b72017-08-24 21:43:24348 window = std::make_unique<SyncedSessionWindow>();
Mikel Astize023c572018-03-28 07:56:56349 window->wrapped_window.window_id = window_id;
Mikel Astiz6d052892018-03-29 07:15:55350 session->synced_window_map[window_id] = window.get();
avi498cabca2016-09-21 19:03:50351 DVLOG(1) << "Putting new window " << window_id << " at " << window.get()
zeac2f678e2015-10-28 06:16:10352 << "in " << (session_tag == local_session_tag_ ? "local session"
353 : session_tag);
[email protected]13daae6d2011-10-04 13:43:12354 }
Mikel Astize023c572018-03-28 07:56:56355 DCHECK_EQ(window->wrapped_window.window_id, window_id);
avi498cabca2016-09-21 19:03:50356 DCHECK(GetSession(session_tag)->windows.end() ==
357 GetSession(session_tag)->windows.find(window_id));
358 GetSession(session_tag)->windows[window_id] = std::move(window);
[email protected]13daae6d2011-10-04 13:43:12359}
360
361void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
Mikel Astize023c572018-03-28 07:56:56362 SessionID window_id,
363 SessionID tab_id) {
Mikel Astiz6d052892018-03-29 07:15:55364 TrackedSession* session = LookupTrackedSession(session_tag);
365 DCHECK(session);
366
[email protected]98f589d2013-08-01 05:14:01367 // We're called here for two reasons. 1) We've received an update to the
zea9b42f4312017-03-01 19:15:44368 // SessionWindow information of a SessionHeader node for a session,
[email protected]98f589d2013-08-01 05:14:01369 // and 2) The SessionHeader node for our local session changed. In both cases
370 // we need to update our tracking state to reflect the change.
371 //
372 // Because the SessionHeader nodes are separate from the individual tab nodes
373 // and we don't store tab_node_ids in the header / SessionWindow specifics,
zea9b42f4312017-03-01 19:15:44374 // the tab_node_ids are not always available when processing headers. We know
375 // that we will eventually process (via GetTab) every single tab node in the
376 // system, so we permit ourselves to just call GetTab and ignore the result,
377 // creating a placeholder SessionTab in the process.
378 sessions::SessionTab* tab_ptr = GetTab(session_tag, tab_id);
[email protected]abb35c12014-08-21 20:13:44379
avi498cabca2016-09-21 19:03:50380 // The tab should be unmapped.
381 std::unique_ptr<sessions::SessionTab> tab;
Mikel Astiz6d052892018-03-29 07:15:55382 auto it = session->unmapped_tabs.find(tab_id);
383 if (it != session->unmapped_tabs.end()) {
avi498cabca2016-09-21 19:03:50384 tab = std::move(it->second);
Mikel Astiz6d052892018-03-29 07:15:55385 session->unmapped_tabs.erase(it);
zea9b42f4312017-03-01 19:15:44386 } else {
387 // The tab has already been mapped, possibly because of the tab node id
388 // being reused across tabs. Find the existing tab and move it to the right
389 // window.
390 for (auto& window_iter_pair : GetSession(session_tag)->windows) {
391 auto tab_iter = std::find_if(
zea325508e2017-03-29 20:11:22392 window_iter_pair.second->wrapped_window.tabs.begin(),
393 window_iter_pair.second->wrapped_window.tabs.end(),
zea9b42f4312017-03-01 19:15:44394 [&tab_ptr](const std::unique_ptr<sessions::SessionTab>& tab) {
395 return tab.get() == tab_ptr;
396 });
zea325508e2017-03-29 20:11:22397 if (tab_iter != window_iter_pair.second->wrapped_window.tabs.end()) {
zea9b42f4312017-03-01 19:15:44398 tab = std::move(*tab_iter);
zea325508e2017-03-29 20:11:22399 window_iter_pair.second->wrapped_window.tabs.erase(tab_iter);
zea9b42f4312017-03-01 19:15:44400
401 DVLOG(1) << "Moving tab " << tab_id << " from window "
402 << window_iter_pair.first << " to " << window_id;
403 break;
404 }
405 }
406 // TODO(zea): remove this once PutTabInWindow isn't crashing anymore.
407 CHECK(tab) << " Unable to find tab " << tab_id
408 << " within unmapped tabs or previously mapped windows."
Sky Malice889822462018-01-03 00:34:04409 << " https://crbug.com/639009";
avi498cabca2016-09-21 19:03:50410 }
[email protected]abb35c12014-08-21 20:13:44411
Mikel Astize023c572018-03-28 07:56:56412 tab->window_id = window_id;
zeac2f678e2015-10-28 06:16:10413 DVLOG(1) << " - tab " << tab_id << " added to window " << window_id;
[email protected]13daae6d2011-10-04 13:43:12414 DCHECK(GetSession(session_tag)->windows.find(window_id) !=
415 GetSession(session_tag)->windows.end());
zea325508e2017-03-29 20:11:22416 GetSession(session_tag)
417 ->windows[window_id]
418 ->wrapped_window.tabs.push_back(std::move(tab));
zea9b42f4312017-03-01 19:15:44419}
420
421void SyncedSessionTracker::OnTabNodeSeen(const std::string& session_tag,
422 int tab_node_id) {
Mikel Astiz6d052892018-03-29 07:15:55423 GetTrackedSession(session_tag)->tab_node_ids.insert(tab_node_id);
[email protected]13daae6d2011-10-04 13:43:12424}
425
zea159246f22016-12-07 00:40:34426sessions::SessionTab* SyncedSessionTracker::GetTab(
[email protected]98f589d2013-08-01 05:14:01427 const std::string& session_tag,
Mikel Astize023c572018-03-28 07:56:56428 SessionID tab_id) {
429 CHECK(tab_id.is_valid()) << "https://crbug.com/639009";
Mikel Astiz6d052892018-03-29 07:15:55430
431 TrackedSession* session = GetTrackedSession(session_tag);
skym199c7fd2016-04-14 19:26:42432 sessions::SessionTab* tab_ptr = nullptr;
Mikel Astiz6d052892018-03-29 07:15:55433 auto iter = session->synced_tab_map.find(tab_id);
434 if (iter != session->synced_tab_map.end()) {
avi498cabca2016-09-21 19:03:50435 tab_ptr = iter->second;
[email protected]98f589d2013-08-01 05:14:01436
[email protected]35d849dc2011-10-21 23:49:05437 if (VLOG_IS_ON(1)) {
438 std::string title;
439 if (tab_ptr->navigations.size() > 0) {
zeac2f678e2015-10-28 06:16:10440 title =
thestigee1440b2016-07-08 01:06:55441 " (" + base::UTF16ToUTF8(tab_ptr->navigations.back().title()) + ")";
[email protected]35d849dc2011-10-21 23:49:05442 }
[email protected]060588c2011-11-29 21:38:41443 DVLOG(1) << "Getting "
zeac2f678e2015-10-28 06:16:10444 << (session_tag == local_session_tag_ ? "local session"
445 : session_tag)
thestigee1440b2016-07-08 01:06:55446 << "'s seen tab " << tab_id << " at " << tab_ptr << " " << title;
[email protected]13daae6d2011-10-04 13:43:12447 }
[email protected]13daae6d2011-10-04 13:43:12448 } else {
Sky Malice5ecd6632018-01-19 18:03:07449 auto tab = std::make_unique<sessions::SessionTab>();
avi498cabca2016-09-21 19:03:50450 tab_ptr = tab.get();
Mikel Astize023c572018-03-28 07:56:56451 tab->tab_id = tab_id;
Mikel Astiz6d052892018-03-29 07:15:55452 session->synced_tab_map[tab_id] = tab_ptr;
453 session->unmapped_tabs[tab_id] = std::move(tab);
[email protected]060588c2011-11-29 21:38:41454 DVLOG(1) << "Getting "
zeac2f678e2015-10-28 06:16:10455 << (session_tag == local_session_tag_ ? "local session"
456 : session_tag)
457 << "'s new tab " << tab_id << " at " << tab_ptr;
[email protected]d70fb362011-06-15 15:07:39458 }
[email protected]13daae6d2011-10-04 13:43:12459 DCHECK(tab_ptr);
Mikel Astize023c572018-03-28 07:56:56460 DCHECK_EQ(tab_ptr->tab_id, tab_id);
[email protected]13daae6d2011-10-04 13:43:12461 return tab_ptr;
[email protected]d70fb362011-06-15 15:07:39462}
463
zea92d4a102017-04-18 06:26:07464void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
zea9b42f4312017-03-01 19:15:44465 CleanupSessionImpl(session_tag);
466}
467
468void SyncedSessionTracker::CleanupLocalTabs(std::set<int>* deleted_node_ids) {
469 DCHECK(!local_session_tag_.empty());
Mikel Astiz6d052892018-03-29 07:15:55470 TrackedSession* session = GetTrackedSession(local_session_tag_);
471 for (const auto& tab_pair : session->unmapped_tabs)
zea9b42f4312017-03-01 19:15:44472 local_tab_pool_.FreeTab(tab_pair.first);
473 CleanupSessionImpl(local_session_tag_);
474 local_tab_pool_.CleanupTabNodes(deleted_node_ids);
475 for (int tab_node_id : *deleted_node_ids) {
Mikel Astiz6d052892018-03-29 07:15:55476 session->tab_node_ids.erase(tab_node_id);
zea9b42f4312017-03-01 19:15:44477 }
478}
479
Mikel Astize023c572018-03-28 07:56:56480bool SyncedSessionTracker::GetTabNodeFromLocalTabId(SessionID tab_id,
zea9b42f4312017-03-01 19:15:44481 int* tab_node_id) {
482 DCHECK(!local_session_tag_.empty());
zea92d4a102017-04-18 06:26:07483 // Ensure a placeholder SessionTab is in place, if not already. Although we
484 // don't need a SessionTab to fulfill this request, this forces the creation
485 // of one if it doesn't already exist. This helps to make sure we're tracking
486 // this |tab_id| if |local_tab_pool_| is, and everyone's data structures are
487 // kept in sync and as consistent as possible.
zea9b42f4312017-03-01 19:15:44488 GetTab(local_session_tag_, tab_id); // Ignore result.
489
490 bool reused_existing_tab =
491 local_tab_pool_.GetTabNodeForTab(tab_id, tab_node_id);
492 DCHECK_NE(TabNodePool::kInvalidTabNodeID, *tab_node_id);
Mikel Astiz6d052892018-03-29 07:15:55493 GetTrackedSession(local_session_tag_)->tab_node_ids.insert(*tab_node_id);
zea9b42f4312017-03-01 19:15:44494 return reused_existing_tab;
495}
496
497bool SyncedSessionTracker::IsLocalTabNodeAssociated(int tab_node_id) {
498 if (tab_node_id == TabNodePool::kInvalidTabNodeID)
499 return false;
Mikel Astize023c572018-03-28 07:56:56500 return local_tab_pool_.GetTabIdFromTabNodeId(tab_node_id).is_valid();
zea9b42f4312017-03-01 19:15:44501}
502
503void SyncedSessionTracker::ReassociateLocalTab(int tab_node_id,
Mikel Astize023c572018-03-28 07:56:56504 SessionID new_tab_id) {
zea9b42f4312017-03-01 19:15:44505 DCHECK(!local_session_tag_.empty());
506 DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
Mikel Astize023c572018-03-28 07:56:56507 DCHECK(new_tab_id.is_valid());
zea9b42f4312017-03-01 19:15:44508
Mikel Astiz6d052892018-03-29 07:15:55509 TrackedSession* session = LookupTrackedSession(local_session_tag_);
510 DCHECK(session);
511
Mikel Astize023c572018-03-28 07:56:56512 SessionID old_tab_id = local_tab_pool_.GetTabIdFromTabNodeId(tab_node_id);
zea783900c2017-05-05 00:46:47513 if (new_tab_id == old_tab_id) {
514 return;
515 }
516
zea9b42f4312017-03-01 19:15:44517 local_tab_pool_.ReassociateTabNode(tab_node_id, new_tab_id);
518
519 sessions::SessionTab* tab_ptr = nullptr;
520
Mikel Astiz6d052892018-03-29 07:15:55521 auto new_tab_iter = session->synced_tab_map.find(new_tab_id);
522 auto old_tab_iter = session->synced_tab_map.find(old_tab_id);
523 if (old_tab_id.is_valid() && old_tab_iter != session->synced_tab_map.end()) {
zea9b42f4312017-03-01 19:15:44524 tab_ptr = old_tab_iter->second;
zea783900c2017-05-05 00:46:47525 DCHECK(tab_ptr);
526
zea9b42f4312017-03-01 19:15:44527 // Remove the tab from the synced tab map under the old id.
Mikel Astiz6d052892018-03-29 07:15:55528 session->synced_tab_map.erase(old_tab_iter);
zea783900c2017-05-05 00:46:47529
Mikel Astiz6d052892018-03-29 07:15:55530 if (new_tab_iter != session->synced_tab_map.end()) {
zea783900c2017-05-05 00:46:47531 // If both the old and the new tab already exist, delete the new tab
532 // and use the old tab in its place.
Mikel Astiz6d052892018-03-29 07:15:55533 auto unmapped_tabs_iter = session->unmapped_tabs.find(new_tab_id);
534 if (unmapped_tabs_iter != session->unmapped_tabs.end()) {
535 session->unmapped_tabs.erase(unmapped_tabs_iter);
zea783900c2017-05-05 00:46:47536 } else {
537 sessions::SessionTab* new_tab_ptr = new_tab_iter->second;
Mikel Astiz6d052892018-03-29 07:15:55538 for (auto& window_iter_pair : session->synced_session.windows) {
zea783900c2017-05-05 00:46:47539 auto& window_tabs = window_iter_pair.second->wrapped_window.tabs;
540 auto tab_iter = std::find_if(
541 window_tabs.begin(), window_tabs.end(),
542 [&new_tab_ptr](const std::unique_ptr<sessions::SessionTab>& tab) {
543 return tab.get() == new_tab_ptr;
544 });
545 if (tab_iter != window_tabs.end()) {
546 window_tabs.erase(tab_iter);
547 break;
548 }
549 }
550 }
551
Mikel Astiz6d052892018-03-29 07:15:55552 session->synced_tab_map.erase(new_tab_iter);
zea783900c2017-05-05 00:46:47553 }
554
555 // If the old tab is unmapped, update the tab id under which it is
556 // indexed.
Mikel Astiz6d052892018-03-29 07:15:55557 auto unmapped_tabs_iter = session->unmapped_tabs.find(old_tab_id);
Mikel Astize023c572018-03-28 07:56:56558 if (old_tab_id.is_valid() &&
Mikel Astiz6d052892018-03-29 07:15:55559 unmapped_tabs_iter != session->unmapped_tabs.end()) {
zea783900c2017-05-05 00:46:47560 std::unique_ptr<sessions::SessionTab> tab =
561 std::move(unmapped_tabs_iter->second);
562 DCHECK_EQ(tab_ptr, tab.get());
Mikel Astiz6d052892018-03-29 07:15:55563 session->unmapped_tabs.erase(unmapped_tabs_iter);
564 session->unmapped_tabs[new_tab_id] = std::move(tab);
zea783900c2017-05-05 00:46:47565 }
566 }
567
568 if (tab_ptr == nullptr) {
zea9b42f4312017-03-01 19:15:44569 // It's possible a placeholder is already in place for the new tab. If so,
570 // reuse it, otherwise create a new one (which will default to unmapped).
571 tab_ptr = GetTab(local_session_tag_, new_tab_id);
572 }
573
zea9b42f4312017-03-01 19:15:44574 // Update the tab id.
Mikel Astize023c572018-03-28 07:56:56575 if (old_tab_id.is_valid()) {
zea9b42f4312017-03-01 19:15:44576 DVLOG(1) << "Remapped tab " << old_tab_id << " with node " << tab_node_id
577 << " to tab " << new_tab_id;
578 } else {
579 DVLOG(1) << "Mapped new tab node " << tab_node_id << " to tab "
580 << new_tab_id;
581 }
Mikel Astize023c572018-03-28 07:56:56582 tab_ptr->tab_id = new_tab_id;
zea9b42f4312017-03-01 19:15:44583
584 // Add the tab back into the tab map with the new id.
Mikel Astiz6d052892018-03-29 07:15:55585 session->synced_tab_map[new_tab_id] = tab_ptr;
586 session->tab_node_ids.insert(tab_node_id);
zea9b42f4312017-03-01 19:15:44587}
588
[email protected]13daae6d2011-10-04 13:43:12589void SyncedSessionTracker::Clear() {
Mikel Astiz6d052892018-03-29 07:15:55590 session_map_.clear();
zea9b42f4312017-03-01 19:15:44591 local_tab_pool_.Clear();
[email protected]819f753f2011-07-15 21:56:02592 local_session_tag_.clear();
[email protected]d70fb362011-06-15 15:07:39593}
594
Mikel Astizf82416e902018-02-27 16:11:13595void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
596 base::Time modification_time,
597 SyncedSessionTracker* tracker) {
598 std::string session_tag = specifics.session_tag();
599 SyncedSession* session = tracker->GetSession(session_tag);
600 if (specifics.has_header()) {
601 // Read in the header data for this session. Header data is
602 // essentially a collection of windows, each of which has an ordered id list
603 // for their tabs.
604
605 if (!IsValidSessionHeader(specifics.header())) {
606 LOG(WARNING) << "Ignoring session node with invalid header "
607 << "and tag " << session_tag << ".";
608 return;
609 }
610
611 // Load (or create) the SyncedSession object for this client.
612 const sync_pb::SessionHeader& header = specifics.header();
613
614 // Reset the tab/window tracking for this session (must do this before
615 // we start calling PutWindowInSession and PutTabInWindow so that all
616 // unused tabs/windows get cleared by the CleanupSession(...) call).
617 tracker->ResetSessionTracking(session_tag);
618
619 PopulateSyncedSessionFromSpecifics(session_tag, header, modification_time,
620 session, tracker);
621
622 // Delete any closed windows and unused tabs as necessary.
623 tracker->CleanupSession(session_tag);
624 } else if (specifics.has_tab()) {
625 const sync_pb::SessionTab& tab_s = specifics.tab();
Mikel Astize023c572018-03-28 07:56:56626 SessionID tab_id = SessionID::FromSerializedValue(tab_s.tab_id());
Mikel Astizf82416e902018-02-27 16:11:13627 DVLOG(1) << "Populating " << session_tag << "'s tab id " << tab_id
628 << " from node " << specifics.tab_node_id();
629
630 // Ensure the tracker is aware of the tab node id. Deleting foreign sessions
631 // requires deleting all relevant tab nodes, and it's easier to track the
632 // tab node ids themselves separately from the tab ids.
633 //
634 // Note that TabIDs are not stable across restarts of a client. Consider
635 // this example with two tabs:
636 //
637 // http://a.com TabID1 --> NodeIDA
638 // http://b.com TabID2 --> NodeIDB
639 //
640 // After restart, tab ids are reallocated. e.g, one possibility:
641 // http://a.com TabID2 --> NodeIDA
642 // http://b.com TabID1 --> NodeIDB
643 //
644 // If that happened on a remote client, here we will see an update to
645 // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
646 // with tab_node_id changing from NodeIDB to NodeIDA.
647 //
648 // We can also wind up here if we created this tab as an out-of-order
649 // update to the header node for this session before actually associating
650 // the tab itself, so the tab node id wasn't available at the time and
651 // is currently kInvalidTabNodeID.
652 //
653 // In both cases, we can safely throw it into the set of node ids.
654 tracker->OnTabNodeSeen(session_tag, specifics.tab_node_id());
655 sessions::SessionTab* tab = tracker->GetTab(session_tag, tab_id);
656 if (!tab->timestamp.is_null() && tab->timestamp > modification_time) {
657 DVLOG(1) << "Ignoring " << session_tag << "'s session tab " << tab_id
658 << " with earlier modification time: " << tab->timestamp
659 << " vs " << modification_time;
660 return;
661 }
662
663 // Update SessionTab based on protobuf.
664 tab->SetFromSyncData(tab_s, modification_time);
665
666 // Update the last modified time.
667 if (session->modified_time < modification_time)
668 session->modified_time = modification_time;
669 } else {
670 LOG(WARNING) << "Ignoring session node with missing header/tab "
671 << "fields and tag " << session_tag << ".";
672 }
673}
674
maxboguea79d99b72016-09-15 15:59:16675} // namespace sync_sessions