blob: 1529dd0938ac1b9156cd98f561902a4b65acefd0 [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 Astizaadda5912018-04-13 11:14:37156std::vector<const SyncedSession*> SyncedSessionTracker::LookupAllSessions(
157 SessionLookup lookup) const {
158 return LookupSessions(lookup, /*exclude_local_session=*/false);
159}
160
Mikel Astiz6d052892018-03-29 07:15:55161std::vector<const SyncedSession*>
162SyncedSessionTracker::LookupAllForeignSessions(SessionLookup lookup) const {
Mikel Astizaadda5912018-04-13 11:14:37163 return LookupSessions(lookup, /*exclude_local_session=*/true);
[email protected]d70fb362011-06-15 15:07:39164}
165
166bool SyncedSessionTracker::LookupSessionWindows(
167 const std::string& session_tag,
skuhneb7409dcf2014-11-14 04:06:55168 std::vector<const sessions::SessionWindow*>* windows) const {
[email protected]d70fb362011-06-15 15:07:39169 DCHECK(windows);
[email protected]13daae6d2011-10-04 13:43:12170 windows->clear();
avi498cabca2016-09-21 19:03:50171
Mikel Astiz6d052892018-03-29 07:15:55172 const TrackedSession* session = LookupTrackedSession(session_tag);
173 if (!session)
174 return false; // We have no record of this session.
175
176 for (const auto& window_pair : session->synced_session.windows)
zea325508e2017-03-29 20:11:22177 windows->push_back(&window_pair.second->wrapped_window);
avi498cabca2016-09-21 19:03:50178
[email protected]d70fb362011-06-15 15:07:39179 return true;
180}
181
Mikel Astiz6d052892018-03-29 07:15:55182const sessions::SessionTab* SyncedSessionTracker::LookupSessionTab(
[email protected]d70fb362011-06-15 15:07:39183 const std::string& tag,
Mikel Astiz6d052892018-03-29 07:15:55184 SessionID tab_id) const {
Mikel Astize023c572018-03-28 07:56:56185 if (!tab_id.is_valid())
Mikel Astiz6d052892018-03-29 07:15:55186 return nullptr;
zea9b42f4312017-03-01 19:15:44187
Mikel Astiz6d052892018-03-29 07:15:55188 const TrackedSession* session = LookupTrackedSession(tag);
189 if (!session)
190 return nullptr; // We have no record of this session.
191
192 auto tab_iter = session->synced_tab_map.find(tab_id);
193 if (tab_iter == session->synced_tab_map.end())
194 return nullptr; // We have no record of this tab.
195
196 return tab_iter->second;
[email protected]d70fb362011-06-15 15:07:39197}
198
Mikel Astiz5e4a71b2018-04-12 11:57:30199std::set<int> SyncedSessionTracker::LookupTabNodeIds(
200 const std::string& session_tag) const {
Mikel Astiz6d052892018-03-29 07:15:55201 const TrackedSession* session = LookupTrackedSession(session_tag);
Mikel Astizdb744b642018-04-20 10:17:14202 return session ? session->tab_node_pool.GetAllTabNodeIds() : std::set<int>();
[email protected]98f589d2013-08-01 05:14:01203}
204
Mikel Astizaadda5912018-04-13 11:14:37205std::vector<const sessions::SessionTab*>
206SyncedSessionTracker::LookupUnmappedTabs(const std::string& session_tag) const {
207 const TrackedSession* session = LookupTrackedSession(session_tag);
208 std::vector<const sessions::SessionTab*> unmapped_tabs;
209 if (session) {
210 for (const auto& unmapped_tab_entry : session->unmapped_tabs) {
211 unmapped_tabs.push_back(unmapped_tab_entry.second.get());
212 }
213 }
214 return unmapped_tabs;
215}
216
Mikel Astiz6d052892018-03-29 07:15:55217const SyncedSession* SyncedSessionTracker::LookupLocalSession() const {
Mikel Astiz5e4a71b2018-04-12 11:57:30218 return LookupSession(local_session_tag_);
219}
220
221const SyncedSession* SyncedSessionTracker::LookupSession(
222 const std::string& session_tag) const {
223 const TrackedSession* session = LookupTrackedSession(session_tag);
Mikel Astiz6d052892018-03-29 07:15:55224 return session ? &session->synced_session : nullptr;
[email protected]85622cd2013-12-18 17:32:27225}
226
maxboguea79d99b72016-09-15 15:59:16227SyncedSession* SyncedSessionTracker::GetSession(
[email protected]d70fb362011-06-15 15:07:39228 const std::string& session_tag) {
Mikel Astiz6d052892018-03-29 07:15:55229 return &GetTrackedSession(session_tag)->synced_session;
[email protected]d70fb362011-06-15 15:07:39230}
231
zea9b42f4312017-03-01 19:15:44232bool SyncedSessionTracker::DeleteForeignSession(
233 const std::string& session_tag) {
234 DCHECK_NE(local_session_tag_, session_tag);
skym199c7fd2016-04-14 19:26:42235
Mikel Astiz6d052892018-03-29 07:15:55236 auto iter = session_map_.find(session_tag);
237 if (iter == session_map_.end())
238 return false;
skym199c7fd2016-04-14 19:26:42239
Mikel Astiz6d052892018-03-29 07:15:55240 // An implicitly created session that has children tabs but no header node
241 // will have never had the device_type changed from unset.
242 const bool header_existed =
243 iter->second.synced_session.device_type != sync_pb::SyncEnums::TYPE_UNSET;
244 // SyncedSession's destructor will trigger deletion of windows which will in
245 // turn trigger the deletion of tabs. This doesn't affect the convenience
246 // maps.
247 session_map_.erase(iter);
skym199c7fd2016-04-14 19:26:42248
249 return header_existed;
[email protected]d70fb362011-06-15 15:07:39250}
251
[email protected]13daae6d2011-10-04 13:43:12252void SyncedSessionTracker::ResetSessionTracking(
253 const std::string& session_tag) {
Mikel Astiz6d052892018-03-29 07:15:55254 TrackedSession* session = GetTrackedSession(session_tag);
[email protected]13daae6d2011-10-04 13:43:12255
Mikel Astiz6d052892018-03-29 07:15:55256 for (auto& window_pair : session->synced_session.windows) {
avi498cabca2016-09-21 19:03:50257 // First unmap the tabs in the window.
zea325508e2017-03-29 20:11:22258 for (auto& tab : window_pair.second->wrapped_window.tabs) {
Mikel Astize023c572018-03-28 07:56:56259 SessionID tab_id = tab->tab_id;
Mikel Astiz6d052892018-03-29 07:15:55260 session->unmapped_tabs[tab_id] = std::move(tab);
[email protected]13daae6d2011-10-04 13:43:12261 }
zea325508e2017-03-29 20:11:22262 window_pair.second->wrapped_window.tabs.clear();
avi498cabca2016-09-21 19:03:50263
264 // Then unmap the window itself.
Mikel Astiz6d052892018-03-29 07:15:55265 session->unmapped_windows[window_pair.first] =
avi498cabca2016-09-21 19:03:50266 std::move(window_pair.second);
[email protected]13daae6d2011-10-04 13:43:12267 }
Mikel Astiz6d052892018-03-29 07:15:55268 session->synced_session.windows.clear();
[email protected]13daae6d2011-10-04 13:43:12269}
270
skym199c7fd2016-04-14 19:26:42271void SyncedSessionTracker::DeleteForeignTab(const std::string& session_tag,
272 int tab_node_id) {
zea9b42f4312017-03-01 19:15:44273 DCHECK_NE(local_session_tag_, session_tag);
Mikel Astiz6d052892018-03-29 07:15:55274 TrackedSession* session = LookupTrackedSession(session_tag);
275 if (session)
Mikel Astizdb744b642018-04-20 10:17:14276 session->tab_node_pool.DeleteTabNode(tab_node_id);
Mikel Astiz6d052892018-03-29 07:15:55277}
278
279const SyncedSessionTracker::TrackedSession*
280SyncedSessionTracker::LookupTrackedSession(
281 const std::string& session_tag) const {
jdoerrie3feb1852018-10-05 12:16:44282 auto it = session_map_.find(session_tag);
Mikel Astiz6d052892018-03-29 07:15:55283 return it == session_map_.end() ? nullptr : &it->second;
284}
285
286SyncedSessionTracker::TrackedSession*
287SyncedSessionTracker::LookupTrackedSession(const std::string& session_tag) {
jdoerrie3feb1852018-10-05 12:16:44288 auto it = session_map_.find(session_tag);
Mikel Astiz6d052892018-03-29 07:15:55289 return it == session_map_.end() ? nullptr : &it->second;
290}
291
292SyncedSessionTracker::TrackedSession* SyncedSessionTracker::GetTrackedSession(
293 const std::string& session_tag) {
294 TrackedSession* session = LookupTrackedSession(session_tag);
295 if (session)
296 return session;
297
298 session = &session_map_[session_tag];
299 DVLOG(1) << "Creating new session with tag " << session_tag << " at "
300 << session;
301 session->synced_session.session_tag = session_tag;
302 return session;
skym199c7fd2016-04-14 19:26:42303}
304
Mikel Astizaadda5912018-04-13 11:14:37305std::vector<const SyncedSession*> SyncedSessionTracker::LookupSessions(
306 SessionLookup lookup,
307 bool exclude_local_session) const {
308 std::vector<const SyncedSession*> sessions;
309 for (const auto& session_pair : session_map_) {
310 const SyncedSession& session = session_pair.second.synced_session;
311 if (lookup == PRESENTABLE && !IsPresentable(sessions_client_, session)) {
312 continue;
313 }
314 if (exclude_local_session && session_pair.first == local_session_tag_) {
315 continue;
316 }
317 sessions.push_back(&session);
318 }
319 return sessions;
320}
321
zea9b42f4312017-03-01 19:15:44322void SyncedSessionTracker::CleanupSessionImpl(const std::string& session_tag) {
Mikel Astiz6d052892018-03-29 07:15:55323 TrackedSession* session = LookupTrackedSession(session_tag);
324 if (!session)
325 return;
[email protected]13daae6d2011-10-04 13:43:12326
Mikel Astiz6d052892018-03-29 07:15:55327 for (const auto& window_pair : session->unmapped_windows)
328 session->synced_window_map.erase(window_pair.first);
329 session->unmapped_windows.clear();
330
Mikel Astizc8e98722018-09-05 11:08:29331 for (const auto& tab_pair : session->unmapped_tabs) {
Mikel Astiz6d052892018-03-29 07:15:55332 session->synced_tab_map.erase(tab_pair.first);
Mikel Astizc8e98722018-09-05 11:08:29333
334 if (session_tag == local_session_tag_)
335 session->tab_node_pool.FreeTab(tab_pair.first);
336 }
Mikel Astiz6d052892018-03-29 07:15:55337 session->unmapped_tabs.clear();
[email protected]13daae6d2011-10-04 13:43:12338}
339
Mikel Astize023c572018-03-28 07:56:56340bool SyncedSessionTracker::IsTabUnmappedForTesting(SessionID tab_id) {
Mikel Astiz6d052892018-03-29 07:15:55341 const TrackedSession* session = LookupTrackedSession(local_session_tag_);
342 if (!session)
343 return false;
344
345 return session->unmapped_tabs.count(tab_id) != 0;
346}
347
[email protected]13daae6d2011-10-04 13:43:12348void SyncedSessionTracker::PutWindowInSession(const std::string& session_tag,
Mikel Astize023c572018-03-28 07:56:56349 SessionID window_id) {
Mikel Astiz6d052892018-03-29 07:15:55350 TrackedSession* session = GetTrackedSession(session_tag);
351 if (session->synced_session.windows.count(window_id) != 0) {
zea92d4a102017-04-18 06:26:07352 DVLOG(1) << "Window " << window_id << " already added to session "
353 << session_tag;
354 return;
355 }
zea325508e2017-03-29 20:11:22356 std::unique_ptr<SyncedSessionWindow> window;
avi498cabca2016-09-21 19:03:50357
Mikel Astiz6d052892018-03-29 07:15:55358 auto iter = session->unmapped_windows.find(window_id);
359 if (iter != session->unmapped_windows.end()) {
360 DCHECK_EQ(session->synced_window_map[window_id], iter->second.get());
avi498cabca2016-09-21 19:03:50361 window = std::move(iter->second);
Mikel Astiz6d052892018-03-29 07:15:55362 session->unmapped_windows.erase(iter);
avi498cabca2016-09-21 19:03:50363 DVLOG(1) << "Putting seen window " << window_id << " at " << window.get()
zeac2f678e2015-10-28 06:16:10364 << "in " << (session_tag == local_session_tag_ ? "local session"
365 : session_tag);
[email protected]d70fb362011-06-15 15:07:39366 } else {
[email protected]13daae6d2011-10-04 13:43:12367 // Create the window.
Sky Malice08a04b72017-08-24 21:43:24368 window = std::make_unique<SyncedSessionWindow>();
Mikel Astize023c572018-03-28 07:56:56369 window->wrapped_window.window_id = window_id;
Mikel Astiz6d052892018-03-29 07:15:55370 session->synced_window_map[window_id] = window.get();
avi498cabca2016-09-21 19:03:50371 DVLOG(1) << "Putting new window " << window_id << " at " << window.get()
zeac2f678e2015-10-28 06:16:10372 << "in " << (session_tag == local_session_tag_ ? "local session"
373 : session_tag);
[email protected]13daae6d2011-10-04 13:43:12374 }
Mikel Astize023c572018-03-28 07:56:56375 DCHECK_EQ(window->wrapped_window.window_id, window_id);
avi498cabca2016-09-21 19:03:50376 DCHECK(GetSession(session_tag)->windows.end() ==
377 GetSession(session_tag)->windows.find(window_id));
378 GetSession(session_tag)->windows[window_id] = std::move(window);
[email protected]13daae6d2011-10-04 13:43:12379}
380
381void SyncedSessionTracker::PutTabInWindow(const std::string& session_tag,
Mikel Astize023c572018-03-28 07:56:56382 SessionID window_id,
383 SessionID tab_id) {
Mikel Astiz6d052892018-03-29 07:15:55384 TrackedSession* session = LookupTrackedSession(session_tag);
385 DCHECK(session);
386
[email protected]98f589d2013-08-01 05:14:01387 // We're called here for two reasons. 1) We've received an update to the
zea9b42f4312017-03-01 19:15:44388 // SessionWindow information of a SessionHeader node for a session,
[email protected]98f589d2013-08-01 05:14:01389 // and 2) The SessionHeader node for our local session changed. In both cases
390 // we need to update our tracking state to reflect the change.
391 //
392 // Because the SessionHeader nodes are separate from the individual tab nodes
393 // and we don't store tab_node_ids in the header / SessionWindow specifics,
zea9b42f4312017-03-01 19:15:44394 // the tab_node_ids are not always available when processing headers. We know
395 // that we will eventually process (via GetTab) every single tab node in the
396 // system, so we permit ourselves to just call GetTab and ignore the result,
397 // creating a placeholder SessionTab in the process.
398 sessions::SessionTab* tab_ptr = GetTab(session_tag, tab_id);
[email protected]abb35c12014-08-21 20:13:44399
avi498cabca2016-09-21 19:03:50400 // The tab should be unmapped.
401 std::unique_ptr<sessions::SessionTab> tab;
Mikel Astiz6d052892018-03-29 07:15:55402 auto it = session->unmapped_tabs.find(tab_id);
403 if (it != session->unmapped_tabs.end()) {
avi498cabca2016-09-21 19:03:50404 tab = std::move(it->second);
Mikel Astiz6d052892018-03-29 07:15:55405 session->unmapped_tabs.erase(it);
zea9b42f4312017-03-01 19:15:44406 } else {
407 // The tab has already been mapped, possibly because of the tab node id
408 // being reused across tabs. Find the existing tab and move it to the right
409 // window.
410 for (auto& window_iter_pair : GetSession(session_tag)->windows) {
411 auto tab_iter = std::find_if(
zea325508e2017-03-29 20:11:22412 window_iter_pair.second->wrapped_window.tabs.begin(),
413 window_iter_pair.second->wrapped_window.tabs.end(),
zea9b42f4312017-03-01 19:15:44414 [&tab_ptr](const std::unique_ptr<sessions::SessionTab>& tab) {
415 return tab.get() == tab_ptr;
416 });
zea325508e2017-03-29 20:11:22417 if (tab_iter != window_iter_pair.second->wrapped_window.tabs.end()) {
zea9b42f4312017-03-01 19:15:44418 tab = std::move(*tab_iter);
zea325508e2017-03-29 20:11:22419 window_iter_pair.second->wrapped_window.tabs.erase(tab_iter);
zea9b42f4312017-03-01 19:15:44420
421 DVLOG(1) << "Moving tab " << tab_id << " from window "
422 << window_iter_pair.first << " to " << window_id;
423 break;
424 }
425 }
426 // TODO(zea): remove this once PutTabInWindow isn't crashing anymore.
427 CHECK(tab) << " Unable to find tab " << tab_id
428 << " within unmapped tabs or previously mapped windows."
Sky Malice889822462018-01-03 00:34:04429 << " https://crbug.com/639009";
avi498cabca2016-09-21 19:03:50430 }
[email protected]abb35c12014-08-21 20:13:44431
Mikel Astize023c572018-03-28 07:56:56432 tab->window_id = window_id;
zeac2f678e2015-10-28 06:16:10433 DVLOG(1) << " - tab " << tab_id << " added to window " << window_id;
[email protected]13daae6d2011-10-04 13:43:12434 DCHECK(GetSession(session_tag)->windows.find(window_id) !=
435 GetSession(session_tag)->windows.end());
zea325508e2017-03-29 20:11:22436 GetSession(session_tag)
437 ->windows[window_id]
438 ->wrapped_window.tabs.push_back(std::move(tab));
zea9b42f4312017-03-01 19:15:44439}
440
441void SyncedSessionTracker::OnTabNodeSeen(const std::string& session_tag,
Mikel Astizdb744b642018-04-20 10:17:14442 int tab_node_id,
443 SessionID tab_id) {
444 // TODO(mastiz): Revisit if the exception for |local_session_tag_| can be
445 // avoided and treat all sessions equally, which ideally involves merging this
446 // function with ReassociateLocalTab().
447 if (session_tag != local_session_tag_ &&
448 tab_node_id != TabNodePool::kInvalidTabNodeID) {
449 GetTrackedSession(session_tag)
450 ->tab_node_pool.ReassociateTabNode(tab_node_id, tab_id);
Mikel Astiz5e4a71b2018-04-12 11:57:30451 }
[email protected]13daae6d2011-10-04 13:43:12452}
453
zea159246f22016-12-07 00:40:34454sessions::SessionTab* SyncedSessionTracker::GetTab(
[email protected]98f589d2013-08-01 05:14:01455 const std::string& session_tag,
Mikel Astize023c572018-03-28 07:56:56456 SessionID tab_id) {
457 CHECK(tab_id.is_valid()) << "https://crbug.com/639009";
Mikel Astiz6d052892018-03-29 07:15:55458
459 TrackedSession* session = GetTrackedSession(session_tag);
skym199c7fd2016-04-14 19:26:42460 sessions::SessionTab* tab_ptr = nullptr;
Mikel Astiz6d052892018-03-29 07:15:55461 auto iter = session->synced_tab_map.find(tab_id);
462 if (iter != session->synced_tab_map.end()) {
avi498cabca2016-09-21 19:03:50463 tab_ptr = iter->second;
[email protected]98f589d2013-08-01 05:14:01464
[email protected]35d849dc2011-10-21 23:49:05465 if (VLOG_IS_ON(1)) {
466 std::string title;
467 if (tab_ptr->navigations.size() > 0) {
zeac2f678e2015-10-28 06:16:10468 title =
thestigee1440b2016-07-08 01:06:55469 " (" + base::UTF16ToUTF8(tab_ptr->navigations.back().title()) + ")";
[email protected]35d849dc2011-10-21 23:49:05470 }
[email protected]060588c2011-11-29 21:38:41471 DVLOG(1) << "Getting "
zeac2f678e2015-10-28 06:16:10472 << (session_tag == local_session_tag_ ? "local session"
473 : session_tag)
thestigee1440b2016-07-08 01:06:55474 << "'s seen tab " << tab_id << " at " << tab_ptr << " " << title;
[email protected]13daae6d2011-10-04 13:43:12475 }
[email protected]13daae6d2011-10-04 13:43:12476 } else {
Sky Malice5ecd6632018-01-19 18:03:07477 auto tab = std::make_unique<sessions::SessionTab>();
avi498cabca2016-09-21 19:03:50478 tab_ptr = tab.get();
Mikel Astize023c572018-03-28 07:56:56479 tab->tab_id = tab_id;
Mikel Astiz6d052892018-03-29 07:15:55480 session->synced_tab_map[tab_id] = tab_ptr;
481 session->unmapped_tabs[tab_id] = std::move(tab);
[email protected]060588c2011-11-29 21:38:41482 DVLOG(1) << "Getting "
zeac2f678e2015-10-28 06:16:10483 << (session_tag == local_session_tag_ ? "local session"
484 : session_tag)
485 << "'s new tab " << tab_id << " at " << tab_ptr;
[email protected]d70fb362011-06-15 15:07:39486 }
[email protected]13daae6d2011-10-04 13:43:12487 DCHECK(tab_ptr);
Mikel Astize023c572018-03-28 07:56:56488 DCHECK_EQ(tab_ptr->tab_id, tab_id);
[email protected]13daae6d2011-10-04 13:43:12489 return tab_ptr;
[email protected]d70fb362011-06-15 15:07:39490}
491
zea92d4a102017-04-18 06:26:07492void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
zea9b42f4312017-03-01 19:15:44493 CleanupSessionImpl(session_tag);
494}
495
496void SyncedSessionTracker::CleanupLocalTabs(std::set<int>* deleted_node_ids) {
497 DCHECK(!local_session_tag_.empty());
Mikel Astiz6d052892018-03-29 07:15:55498 TrackedSession* session = GetTrackedSession(local_session_tag_);
zea9b42f4312017-03-01 19:15:44499 CleanupSessionImpl(local_session_tag_);
Mikel Astizdb744b642018-04-20 10:17:14500 session->tab_node_pool.CleanupTabNodes(deleted_node_ids);
zea9b42f4312017-03-01 19:15:44501}
502
Mikel Astizdb744b642018-04-20 10:17:14503int SyncedSessionTracker::LookupTabNodeFromTabId(const std::string& session_tag,
504 SessionID tab_id) const {
505 const TrackedSession* session = LookupTrackedSession(session_tag);
506 DCHECK(session);
507 return session->tab_node_pool.GetTabNodeIdFromTabId(tab_id);
508}
509
510SessionID SyncedSessionTracker::LookupTabIdFromTabNodeId(
511 const std::string& session_tag,
512 int tab_node_id) const {
513 const TrackedSession* session = LookupTrackedSession(session_tag);
514 DCHECK(session);
515 return session->tab_node_pool.GetTabIdFromTabNodeId(tab_node_id);
Mikel Astizaadda5912018-04-13 11:14:37516}
517
Mikel Astizcb491c4cb2018-05-24 13:08:26518int SyncedSessionTracker::AssociateLocalTabWithFreeTabNode(SessionID tab_id) {
zea9b42f4312017-03-01 19:15:44519 DCHECK(!local_session_tag_.empty());
Mikel Astizdb744b642018-04-20 10:17:14520 TrackedSession* session = GetTrackedSession(local_session_tag_);
521
zea92d4a102017-04-18 06:26:07522 // Ensure a placeholder SessionTab is in place, if not already. Although we
523 // don't need a SessionTab to fulfill this request, this forces the creation
524 // of one if it doesn't already exist. This helps to make sure we're tracking
Mikel Astizdb744b642018-04-20 10:17:14525 // this |tab_id| if TabNodePool is, and everyone's data structures are kept in
526 // sync and as consistent as possible.
zea9b42f4312017-03-01 19:15:44527 GetTab(local_session_tag_, tab_id); // Ignore result.
528
Mikel Astizcb491c4cb2018-05-24 13:08:26529 return session->tab_node_pool.AssociateWithFreeTabNode(tab_id);
zea9b42f4312017-03-01 19:15:44530}
531
zea9b42f4312017-03-01 19:15:44532void SyncedSessionTracker::ReassociateLocalTab(int tab_node_id,
Mikel Astize023c572018-03-28 07:56:56533 SessionID new_tab_id) {
zea9b42f4312017-03-01 19:15:44534 DCHECK(!local_session_tag_.empty());
535 DCHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id);
Mikel Astize023c572018-03-28 07:56:56536 DCHECK(new_tab_id.is_valid());
zea9b42f4312017-03-01 19:15:44537
Mikel Astiz6d052892018-03-29 07:15:55538 TrackedSession* session = LookupTrackedSession(local_session_tag_);
539 DCHECK(session);
540
Mikel Astizdb744b642018-04-20 10:17:14541 SessionID old_tab_id =
542 session->tab_node_pool.GetTabIdFromTabNodeId(tab_node_id);
zea783900c2017-05-05 00:46:47543 if (new_tab_id == old_tab_id) {
544 return;
545 }
546
Mikel Astizdb744b642018-04-20 10:17:14547 session->tab_node_pool.ReassociateTabNode(tab_node_id, new_tab_id);
zea9b42f4312017-03-01 19:15:44548
549 sessions::SessionTab* tab_ptr = nullptr;
550
Mikel Astiz6d052892018-03-29 07:15:55551 auto new_tab_iter = session->synced_tab_map.find(new_tab_id);
552 auto old_tab_iter = session->synced_tab_map.find(old_tab_id);
553 if (old_tab_id.is_valid() && old_tab_iter != session->synced_tab_map.end()) {
zea9b42f4312017-03-01 19:15:44554 tab_ptr = old_tab_iter->second;
zea783900c2017-05-05 00:46:47555 DCHECK(tab_ptr);
556
zea9b42f4312017-03-01 19:15:44557 // Remove the tab from the synced tab map under the old id.
Mikel Astiz6d052892018-03-29 07:15:55558 session->synced_tab_map.erase(old_tab_iter);
zea783900c2017-05-05 00:46:47559
Mikel Astiz6d052892018-03-29 07:15:55560 if (new_tab_iter != session->synced_tab_map.end()) {
zea783900c2017-05-05 00:46:47561 // If both the old and the new tab already exist, delete the new tab
562 // and use the old tab in its place.
Mikel Astiz6d052892018-03-29 07:15:55563 auto unmapped_tabs_iter = session->unmapped_tabs.find(new_tab_id);
564 if (unmapped_tabs_iter != session->unmapped_tabs.end()) {
565 session->unmapped_tabs.erase(unmapped_tabs_iter);
zea783900c2017-05-05 00:46:47566 } else {
567 sessions::SessionTab* new_tab_ptr = new_tab_iter->second;
Mikel Astiz6d052892018-03-29 07:15:55568 for (auto& window_iter_pair : session->synced_session.windows) {
zea783900c2017-05-05 00:46:47569 auto& window_tabs = window_iter_pair.second->wrapped_window.tabs;
570 auto tab_iter = std::find_if(
571 window_tabs.begin(), window_tabs.end(),
572 [&new_tab_ptr](const std::unique_ptr<sessions::SessionTab>& tab) {
573 return tab.get() == new_tab_ptr;
574 });
575 if (tab_iter != window_tabs.end()) {
576 window_tabs.erase(tab_iter);
577 break;
578 }
579 }
580 }
581
Mikel Astiz6d052892018-03-29 07:15:55582 session->synced_tab_map.erase(new_tab_iter);
zea783900c2017-05-05 00:46:47583 }
584
585 // If the old tab is unmapped, update the tab id under which it is
586 // indexed.
Mikel Astiz6d052892018-03-29 07:15:55587 auto unmapped_tabs_iter = session->unmapped_tabs.find(old_tab_id);
Mikel Astize023c572018-03-28 07:56:56588 if (old_tab_id.is_valid() &&
Mikel Astiz6d052892018-03-29 07:15:55589 unmapped_tabs_iter != session->unmapped_tabs.end()) {
zea783900c2017-05-05 00:46:47590 std::unique_ptr<sessions::SessionTab> tab =
591 std::move(unmapped_tabs_iter->second);
592 DCHECK_EQ(tab_ptr, tab.get());
Mikel Astiz6d052892018-03-29 07:15:55593 session->unmapped_tabs.erase(unmapped_tabs_iter);
594 session->unmapped_tabs[new_tab_id] = std::move(tab);
zea783900c2017-05-05 00:46:47595 }
596 }
597
598 if (tab_ptr == nullptr) {
zea9b42f4312017-03-01 19:15:44599 // It's possible a placeholder is already in place for the new tab. If so,
600 // reuse it, otherwise create a new one (which will default to unmapped).
601 tab_ptr = GetTab(local_session_tag_, new_tab_id);
602 }
603
zea9b42f4312017-03-01 19:15:44604 // Update the tab id.
Mikel Astize023c572018-03-28 07:56:56605 if (old_tab_id.is_valid()) {
zea9b42f4312017-03-01 19:15:44606 DVLOG(1) << "Remapped tab " << old_tab_id << " with node " << tab_node_id
607 << " to tab " << new_tab_id;
608 } else {
609 DVLOG(1) << "Mapped new tab node " << tab_node_id << " to tab "
610 << new_tab_id;
611 }
Mikel Astize023c572018-03-28 07:56:56612 tab_ptr->tab_id = new_tab_id;
zea9b42f4312017-03-01 19:15:44613
614 // Add the tab back into the tab map with the new id.
Mikel Astiz6d052892018-03-29 07:15:55615 session->synced_tab_map[new_tab_id] = tab_ptr;
zea9b42f4312017-03-01 19:15:44616}
617
[email protected]13daae6d2011-10-04 13:43:12618void SyncedSessionTracker::Clear() {
Mikel Astiz6d052892018-03-29 07:15:55619 session_map_.clear();
[email protected]819f753f2011-07-15 21:56:02620 local_session_tag_.clear();
[email protected]d70fb362011-06-15 15:07:39621}
622
Mikel Astizf82416e902018-02-27 16:11:13623void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
624 base::Time modification_time,
625 SyncedSessionTracker* tracker) {
626 std::string session_tag = specifics.session_tag();
627 SyncedSession* session = tracker->GetSession(session_tag);
628 if (specifics.has_header()) {
629 // Read in the header data for this session. Header data is
630 // essentially a collection of windows, each of which has an ordered id list
631 // for their tabs.
632
633 if (!IsValidSessionHeader(specifics.header())) {
Mikel Astiz64d22392018-05-09 08:33:07634 DLOG(WARNING) << "Ignoring session node with invalid header "
635 << "and tag " << session_tag << ".";
Mikel Astizf82416e902018-02-27 16:11:13636 return;
637 }
638
639 // Load (or create) the SyncedSession object for this client.
640 const sync_pb::SessionHeader& header = specifics.header();
641
642 // Reset the tab/window tracking for this session (must do this before
643 // we start calling PutWindowInSession and PutTabInWindow so that all
644 // unused tabs/windows get cleared by the CleanupSession(...) call).
645 tracker->ResetSessionTracking(session_tag);
646
647 PopulateSyncedSessionFromSpecifics(session_tag, header, modification_time,
648 session, tracker);
649
650 // Delete any closed windows and unused tabs as necessary.
651 tracker->CleanupSession(session_tag);
652 } else if (specifics.has_tab()) {
653 const sync_pb::SessionTab& tab_s = specifics.tab();
Mikel Astize023c572018-03-28 07:56:56654 SessionID tab_id = SessionID::FromSerializedValue(tab_s.tab_id());
Mikel Astiz64d22392018-05-09 08:33:07655 if (!tab_id.is_valid()) {
656 DLOG(WARNING) << "Ignoring session tab with invalid tab ID for session "
657 << "tag " << session_tag << " and node ID "
658 << specifics.tab_node_id() << ".";
659 return;
660 }
661
Mikel Astizf82416e902018-02-27 16:11:13662 DVLOG(1) << "Populating " << session_tag << "'s tab id " << tab_id
663 << " from node " << specifics.tab_node_id();
664
665 // Ensure the tracker is aware of the tab node id. Deleting foreign sessions
666 // requires deleting all relevant tab nodes, and it's easier to track the
667 // tab node ids themselves separately from the tab ids.
668 //
669 // Note that TabIDs are not stable across restarts of a client. Consider
670 // this example with two tabs:
671 //
672 // http://a.com TabID1 --> NodeIDA
673 // http://b.com TabID2 --> NodeIDB
674 //
675 // After restart, tab ids are reallocated. e.g, one possibility:
676 // http://a.com TabID2 --> NodeIDA
677 // http://b.com TabID1 --> NodeIDB
678 //
679 // If that happened on a remote client, here we will see an update to
680 // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2
681 // with tab_node_id changing from NodeIDB to NodeIDA.
682 //
683 // We can also wind up here if we created this tab as an out-of-order
684 // update to the header node for this session before actually associating
685 // the tab itself, so the tab node id wasn't available at the time and
686 // is currently kInvalidTabNodeID.
687 //
688 // In both cases, we can safely throw it into the set of node ids.
Mikel Astizdb744b642018-04-20 10:17:14689 tracker->OnTabNodeSeen(session_tag, specifics.tab_node_id(), tab_id);
Mikel Astizf82416e902018-02-27 16:11:13690 sessions::SessionTab* tab = tracker->GetTab(session_tag, tab_id);
691 if (!tab->timestamp.is_null() && tab->timestamp > modification_time) {
692 DVLOG(1) << "Ignoring " << session_tag << "'s session tab " << tab_id
693 << " with earlier modification time: " << tab->timestamp
694 << " vs " << modification_time;
695 return;
696 }
697
698 // Update SessionTab based on protobuf.
Mikel Astiz23afb112018-07-09 18:32:27699 SetSessionTabFromSyncData(tab_s, modification_time, tab);
Mikel Astizf82416e902018-02-27 16:11:13700
701 // Update the last modified time.
702 if (session->modified_time < modification_time)
703 session->modified_time = modification_time;
704 } else {
705 LOG(WARNING) << "Ignoring session node with missing header/tab "
706 << "fields and tag " << session_tag << ".";
707 }
708}
709
Mikel Astizdb744b642018-04-20 10:17:14710void SerializeTrackerToSpecifics(
711 const SyncedSessionTracker& tracker,
712 const base::RepeatingCallback<void(const std::string& session_name,
713 sync_pb::SessionSpecifics* specifics)>&
714 output_cb) {
715 std::map<std::string, std::set<int>> session_tag_to_node_ids;
716 for (const SyncedSession* session :
717 tracker.LookupAllSessions(SyncedSessionTracker::RAW)) {
718 // Request all tabs.
719 session_tag_to_node_ids[session->session_tag] =
720 tracker.LookupTabNodeIds(session->session_tag);
721 // Request the header too.
722 session_tag_to_node_ids[session->session_tag].insert(
723 TabNodePool::kInvalidTabNodeID);
724 }
725 SerializePartialTrackerToSpecifics(tracker, session_tag_to_node_ids,
726 output_cb);
727}
728
729void SerializePartialTrackerToSpecifics(
730 const SyncedSessionTracker& tracker,
731 const std::map<std::string, std::set<int>>& session_tag_to_node_ids,
732 const base::RepeatingCallback<void(const std::string& session_name,
733 sync_pb::SessionSpecifics* specifics)>&
734 output_cb) {
735 for (const auto& session_entry : session_tag_to_node_ids) {
736 const std::string& session_tag = session_entry.first;
737 const SyncedSession* session = tracker.LookupSession(session_tag);
738 if (!session) {
739 // Unknown session.
740 continue;
741 }
742
743 const std::set<int> known_tab_node_ids =
744 tracker.LookupTabNodeIds(session_tag);
745
746 for (int tab_node_id : session_entry.second) {
747 // Header entity.
748 if (tab_node_id == TabNodePool::kInvalidTabNodeID) {
749 sync_pb::SessionSpecifics header_pb;
750 header_pb.set_session_tag(session_tag);
751 session->ToSessionHeaderProto().Swap(header_pb.mutable_header());
752 output_cb.Run(session->session_name, &header_pb);
753 continue;
754 }
755
756 // Check if |tab_node_id| is known by the tracker.
757 if (known_tab_node_ids.count(tab_node_id) == 0) {
758 continue;
759 }
760
761 // Tab entities.
762 const SessionID tab_id =
763 tracker.LookupTabIdFromTabNodeId(session_tag, tab_node_id);
Mikel Astizbdb88462018-05-09 13:46:00764 if (!tab_id.is_valid()) {
765 // This can be the case for tabs that were unmapped because we received
766 // a new foreign tab with the same tab ID (the last one wins), so we
767 // don't remember the tab ID for the original |tab_node_id|. Instead of
768 // returning partially populated SessionSpecifics (without tab ID), we
769 // simply drop them, because older clients don't handle well such
770 // invalid specifics.
771 continue;
772 }
773
Mikel Astizb2243492018-05-04 21:06:20774 const sessions::SessionTab* tab =
775 tracker.LookupSessionTab(session_tag, tab_id);
776 if (tab) {
777 // Associated/mapped tab node.
Mikel Astizdb744b642018-04-20 10:17:14778 sync_pb::SessionSpecifics tab_pb;
779 tab_pb.set_session_tag(session_tag);
780 tab_pb.set_tab_node_id(tab_node_id);
Mikel Astiz23afb112018-07-09 18:32:27781 SessionTabToSyncData(*tab).Swap(tab_pb.mutable_tab());
Mikel Astizdb744b642018-04-20 10:17:14782 output_cb.Run(session->session_name, &tab_pb);
783 continue;
784 }
785
Mikel Astizbdb88462018-05-09 13:46:00786 // Create entities for unmapped tabs nodes.
Mikel Astizdb744b642018-04-20 10:17:14787 sync_pb::SessionSpecifics tab_pb;
788 tab_pb.set_tab_node_id(tab_node_id);
789 tab_pb.set_session_tag(session_tag);
Mikel Astizbdb88462018-05-09 13:46:00790 tab_pb.mutable_tab()->set_tab_id(tab_id.id());
Mikel Astizdb744b642018-04-20 10:17:14791 output_cb.Run(session->session_name, &tab_pb);
792 }
793 }
794}
795
maxboguea79d99b72016-09-15 15:59:16796} // namespace sync_sessions