OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/sync_sessions/sessions_sync_manager.h" | 5 #include "components/sync_sessions/sessions_sync_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/format_macros.h" | |
11 #include "base/logging.h" | |
12 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
13 #include "base/metrics/field_trial.h" | 11 #include "base/metrics/field_trial.h" |
14 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
15 #include "base/strings/stringprintf.h" | |
16 #include "build/build_config.h" | 13 #include "build/build_config.h" |
17 #include "components/sync/base/hash_util.h" | 14 #include "components/sync/base/hash_util.h" |
18 #include "components/sync/device_info/local_device_info_provider.h" | 15 #include "components/sync/device_info/local_device_info_provider.h" |
19 #include "components/sync/model/sync_error.h" | 16 #include "components/sync/model/sync_error.h" |
20 #include "components/sync/model/sync_error_factory.h" | 17 #include "components/sync/model/sync_error_factory.h" |
21 #include "components/sync/model/sync_merge_result.h" | 18 #include "components/sync/model/sync_merge_result.h" |
22 #include "components/sync/model/time.h" | 19 #include "components/sync/model/time.h" |
23 #include "components/sync_sessions/sync_sessions_client.h" | 20 #include "components/sync_sessions/sync_sessions_client.h" |
24 #include "components/sync_sessions/synced_tab_delegate.h" | 21 #include "components/sync_sessions/synced_tab_delegate.h" |
25 #include "components/sync_sessions/synced_window_delegate.h" | 22 #include "components/sync_sessions/synced_window_delegate.h" |
26 #include "components/sync_sessions/synced_window_delegates_getter.h" | 23 #include "components/sync_sessions/synced_window_delegates_getter.h" |
27 #include "components/sync_sessions/tab_node_pool.h" | |
28 #include "components/variations/variations_associated_data.h" | 24 #include "components/variations/variations_associated_data.h" |
29 | 25 |
30 using sessions::SerializedNavigationEntry; | 26 using sessions::SerializedNavigationEntry; |
31 using syncer::DeviceInfo; | 27 using syncer::DeviceInfo; |
32 using syncer::LocalDeviceInfoProvider; | 28 using syncer::LocalDeviceInfoProvider; |
33 using syncer::SyncChange; | 29 using syncer::SyncChange; |
34 using syncer::SyncData; | 30 using syncer::SyncData; |
35 | 31 |
36 namespace sync_sessions { | 32 namespace sync_sessions { |
37 | 33 |
(...skipping 21 matching lines...) Expand all Loading... |
59 return t1->timestamp > t2->timestamp; | 55 return t1->timestamp > t2->timestamp; |
60 } | 56 } |
61 | 57 |
62 // Comparator function for use with std::sort that will sort sessions by | 58 // Comparator function for use with std::sort that will sort sessions by |
63 // descending modified_time (i.e., most recent first). | 59 // descending modified_time (i.e., most recent first). |
64 bool SessionsRecencyComparator(const SyncedSession* s1, | 60 bool SessionsRecencyComparator(const SyncedSession* s1, |
65 const SyncedSession* s2) { | 61 const SyncedSession* s2) { |
66 return s1->modified_time > s2->modified_time; | 62 return s1->modified_time > s2->modified_time; |
67 } | 63 } |
68 | 64 |
69 std::string TabNodeIdToTag(const std::string& machine_tag, int tab_node_id) { | |
70 CHECK_GT(tab_node_id, TabNodePool::kInvalidTabNodeID) << "crbug.com/673618"; | |
71 return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id); | |
72 } | |
73 | |
74 std::string TagFromSpecifics(const sync_pb::SessionSpecifics& specifics) { | 65 std::string TagFromSpecifics(const sync_pb::SessionSpecifics& specifics) { |
75 if (specifics.has_header()) { | 66 if (specifics.has_header()) { |
76 return specifics.session_tag(); | 67 return specifics.session_tag(); |
77 } else if (specifics.has_tab()) { | 68 } else if (specifics.has_tab()) { |
78 return TabNodeIdToTag(specifics.session_tag(), specifics.tab_node_id()); | 69 return TabNodePool::TabIdToTag(specifics.session_tag(), |
| 70 specifics.tab_node_id()); |
79 } else { | 71 } else { |
80 return std::string(); | 72 return std::string(); |
81 } | 73 } |
82 } | 74 } |
83 | 75 |
84 sync_pb::SessionSpecifics SessionTabToSpecifics( | |
85 const sessions::SessionTab& session_tab, | |
86 const std::string& local_tag, | |
87 int tab_node_id) { | |
88 sync_pb::SessionSpecifics specifics; | |
89 specifics.mutable_tab()->CopyFrom(session_tab.ToSyncData()); | |
90 specifics.set_session_tag(local_tag); | |
91 specifics.set_tab_node_id(tab_node_id); | |
92 return specifics; | |
93 } | |
94 | |
95 void AppendDeletionsForTabNodes(const std::set<int>& tab_node_ids, | |
96 const std::string& machine_tag, | |
97 syncer::SyncChangeList* change_output) { | |
98 for (std::set<int>::const_iterator it = tab_node_ids.begin(); | |
99 it != tab_node_ids.end(); ++it) { | |
100 change_output->push_back(syncer::SyncChange( | |
101 FROM_HERE, SyncChange::ACTION_DELETE, | |
102 SyncData::CreateLocalDelete(TabNodeIdToTag(machine_tag, *it), | |
103 syncer::SESSIONS))); | |
104 } | |
105 } | |
106 | |
107 // Ensure that the tab id is not invalid and is not already synced (which | |
108 // can confuse the logic that tracks whether tabs are mapped or unmapped). | |
109 bool ShouldSyncTabId( | |
110 SessionID::id_type tab_id, | |
111 const google::protobuf::RepeatedField<int>& synced_tab_ids) { | |
112 if (tab_id == TabNodePool::kInvalidTabID) | |
113 return false; | |
114 | |
115 for (auto synced_tab_id : synced_tab_ids) { | |
116 if (tab_id == synced_tab_id) | |
117 return false; | |
118 } | |
119 | |
120 return true; | |
121 } | |
122 | |
123 } // namespace | 76 } // namespace |
124 | 77 |
125 // |local_device| is owned by ProfileSyncService, its lifetime exceeds | 78 // |local_device| is owned by ProfileSyncService, its lifetime exceeds |
126 // lifetime of SessionSyncManager. | 79 // lifetime of SessionSyncManager. |
127 SessionsSyncManager::SessionsSyncManager( | 80 SessionsSyncManager::SessionsSyncManager( |
128 sync_sessions::SyncSessionsClient* sessions_client, | 81 sync_sessions::SyncSessionsClient* sessions_client, |
129 syncer::SyncPrefs* sync_prefs, | 82 syncer::SyncPrefs* sync_prefs, |
130 LocalDeviceInfoProvider* local_device, | 83 LocalDeviceInfoProvider* local_device, |
131 std::unique_ptr<LocalSessionEventRouter> router, | 84 std::unique_ptr<LocalSessionEventRouter> router, |
132 const base::Closure& sessions_updated_callback, | 85 const base::Closure& sessions_updated_callback, |
(...skipping 24 matching lines...) Expand all Loading... |
157 return machine_tag; | 110 return machine_tag; |
158 } | 111 } |
159 | 112 |
160 syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing( | 113 syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing( |
161 syncer::ModelType type, | 114 syncer::ModelType type, |
162 const syncer::SyncDataList& initial_sync_data, | 115 const syncer::SyncDataList& initial_sync_data, |
163 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, | 116 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, |
164 std::unique_ptr<syncer::SyncErrorFactory> error_handler) { | 117 std::unique_ptr<syncer::SyncErrorFactory> error_handler) { |
165 syncer::SyncMergeResult merge_result(type); | 118 syncer::SyncMergeResult merge_result(type); |
166 DCHECK(session_tracker_.Empty()); | 119 DCHECK(session_tracker_.Empty()); |
| 120 DCHECK_EQ(0U, local_tab_pool_.Capacity()); |
167 | 121 |
168 error_handler_ = std::move(error_handler); | 122 error_handler_ = std::move(error_handler); |
169 sync_processor_ = std::move(sync_processor); | 123 sync_processor_ = std::move(sync_processor); |
170 | 124 |
171 // SessionDataTypeController ensures that the local device info | 125 // SessionDataTypeController ensures that the local device info |
172 // is available before activating this datatype. | 126 // is available before activating this datatype. |
173 DCHECK(local_device_); | 127 DCHECK(local_device_); |
174 const DeviceInfo* local_device_info = local_device_->GetLocalDeviceInfo(); | 128 const DeviceInfo* local_device_info = local_device_->GetLocalDeviceInfo(); |
175 if (!local_device_info) { | 129 if (!local_device_info) { |
176 merge_result.set_error(error_handler_->CreateAndUploadError( | 130 merge_result.set_error(error_handler_->CreateAndUploadError( |
(...skipping 15 matching lines...) Expand all Loading... |
192 | 146 |
193 local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID; | 147 local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID; |
194 | 148 |
195 // Make sure we have a machine tag. We do this now (versus earlier) as it's | 149 // Make sure we have a machine tag. We do this now (versus earlier) as it's |
196 // a conveniently safe time to assert sync is ready and the cache_guid is | 150 // a conveniently safe time to assert sync is ready and the cache_guid is |
197 // initialized. | 151 // initialized. |
198 if (current_machine_tag_.empty()) { | 152 if (current_machine_tag_.empty()) { |
199 InitializeCurrentMachineTag(local_device_->GetLocalSyncCacheGUID()); | 153 InitializeCurrentMachineTag(local_device_->GetLocalSyncCacheGUID()); |
200 } | 154 } |
201 | 155 |
202 session_tracker_.SetLocalSessionTag(current_machine_tag()); | 156 session_tracker_.SetLocalSessionTag(current_machine_tag_); |
203 | 157 |
204 syncer::SyncChangeList new_changes; | 158 syncer::SyncChangeList new_changes; |
205 | 159 |
206 // First, we iterate over sync data to update our session_tracker_. | 160 // First, we iterate over sync data to update our session_tracker_. |
207 if (!InitFromSyncModel(initial_sync_data, &new_changes)) { | 161 syncer::SyncDataList restored_tabs; |
| 162 if (!InitFromSyncModel(initial_sync_data, &restored_tabs, &new_changes)) { |
208 // The sync db didn't have a header node for us. Create one. | 163 // The sync db didn't have a header node for us. Create one. |
209 sync_pb::EntitySpecifics specifics; | 164 sync_pb::EntitySpecifics specifics; |
210 sync_pb::SessionSpecifics* base_specifics = specifics.mutable_session(); | 165 sync_pb::SessionSpecifics* base_specifics = specifics.mutable_session(); |
211 base_specifics->set_session_tag(current_machine_tag()); | 166 base_specifics->set_session_tag(current_machine_tag()); |
212 sync_pb::SessionHeader* header_s = base_specifics->mutable_header(); | 167 sync_pb::SessionHeader* header_s = base_specifics->mutable_header(); |
213 header_s->set_client_name(current_session_name_); | 168 header_s->set_client_name(current_session_name_); |
214 header_s->set_device_type(current_device_type_); | 169 header_s->set_device_type(current_device_type_); |
215 syncer::SyncData data = syncer::SyncData::CreateLocalData( | 170 syncer::SyncData data = syncer::SyncData::CreateLocalData( |
216 current_machine_tag(), current_session_name_, specifics); | 171 current_machine_tag(), current_session_name_, specifics); |
217 new_changes.push_back( | 172 new_changes.push_back( |
218 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, data)); | 173 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, data)); |
219 } | 174 } |
220 | 175 |
221 #if defined(OS_ANDROID) | 176 #if defined(OS_ANDROID) |
222 std::string sync_machine_tag( | 177 std::string sync_machine_tag( |
223 BuildMachineTag(local_device_->GetLocalSyncCacheGUID())); | 178 BuildMachineTag(local_device_->GetLocalSyncCacheGUID())); |
224 if (current_machine_tag().compare(sync_machine_tag) != 0) | 179 if (current_machine_tag_.compare(sync_machine_tag) != 0) |
225 DeleteForeignSessionInternal(sync_machine_tag, &new_changes); | 180 DeleteForeignSessionInternal(sync_machine_tag, &new_changes); |
226 #endif | 181 #endif |
227 | 182 |
228 // Check if anything has changed on the local client side. | 183 // Check if anything has changed on the local client side. |
229 AssociateWindows(RELOAD_TABS, &new_changes); | 184 AssociateWindows(RELOAD_TABS, restored_tabs, &new_changes); |
230 local_tab_pool_out_of_sync_ = false; | 185 local_tab_pool_out_of_sync_ = false; |
231 | 186 |
232 merge_result.set_error( | 187 merge_result.set_error( |
233 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); | 188 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); |
234 | 189 |
235 local_event_router_->StartRoutingTo(this); | 190 local_event_router_->StartRoutingTo(this); |
236 return merge_result; | 191 return merge_result; |
237 } | 192 } |
238 | 193 |
239 void SessionsSyncManager::AssociateWindows( | 194 void SessionsSyncManager::AssociateWindows( |
240 ReloadTabsOption option, | 195 ReloadTabsOption option, |
| 196 const syncer::SyncDataList& restored_tabs, |
241 syncer::SyncChangeList* change_output) { | 197 syncer::SyncChangeList* change_output) { |
242 const std::string local_tag = current_machine_tag(); | 198 const std::string local_tag = current_machine_tag(); |
243 sync_pb::SessionSpecifics specifics; | 199 sync_pb::SessionSpecifics specifics; |
244 specifics.set_session_tag(local_tag); | 200 specifics.set_session_tag(local_tag); |
245 sync_pb::SessionHeader* header_s = specifics.mutable_header(); | 201 sync_pb::SessionHeader* header_s = specifics.mutable_header(); |
246 SyncedSession* current_session = session_tracker_.GetSession(local_tag); | 202 SyncedSession* current_session = session_tracker_.GetSession(local_tag); |
247 current_session->modified_time = base::Time::Now(); | 203 current_session->modified_time = base::Time::Now(); |
248 header_s->set_client_name(current_session_name_); | 204 header_s->set_client_name(current_session_name_); |
249 header_s->set_device_type(current_device_type_); | 205 header_s->set_device_type(current_device_type_); |
250 | 206 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 | 254 |
299 bool found_tabs = false; | 255 bool found_tabs = false; |
300 for (int j = 0; j < window_delegate->GetTabCount(); ++j) { | 256 for (int j = 0; j < window_delegate->GetTabCount(); ++j) { |
301 SessionID::id_type tab_id = window_delegate->GetTabIdAt(j); | 257 SessionID::id_type tab_id = window_delegate->GetTabIdAt(j); |
302 SyncedTabDelegate* synced_tab = window_delegate->GetTabAt(j); | 258 SyncedTabDelegate* synced_tab = window_delegate->GetTabAt(j); |
303 | 259 |
304 // GetTabAt can return a null tab; in that case just skip it. | 260 // GetTabAt can return a null tab; in that case just skip it. |
305 if (!synced_tab) | 261 if (!synced_tab) |
306 continue; | 262 continue; |
307 | 263 |
308 if (!ShouldSyncTabId(tab_id, window_s.tab())) { | 264 if (synced_tab->IsPlaceholderTab()) { |
309 LOG(ERROR) << "Not syncing invalid/duplicate tab with id " << tab_id; | 265 // For tabs without WebContents update the |tab_id| and |window_id|, |
| 266 // as it could have changed after a session restore. |
| 267 // Note: We cannot check if a tab is valid if it has no WebContents. |
| 268 // We assume any such tab is valid and leave the contents of |
| 269 // corresponding sync node unchanged. |
| 270 if (synced_tab->GetSyncId() > TabNodePool::kInvalidTabNodeID && |
| 271 tab_id > TabNodePool::kInvalidTabID) { |
| 272 AssociateRestoredPlaceholderTab(*synced_tab, tab_id, window_id, |
| 273 restored_tabs, change_output); |
| 274 found_tabs = true; |
| 275 window_s.add_tab(tab_id); |
| 276 } |
310 continue; | 277 continue; |
311 } | 278 } |
312 | 279 |
313 // Placeholder tabs are those without WebContents, either because they | 280 if (RELOAD_TABS == option) |
314 // were never loaded into memory or they were evicted from memory | |
315 // (typically only on Android devices). They only have a tab id, window | |
316 // id, and a saved synced id (corresponding to the tab node id). Note | |
317 // that only placeholders have this sync id, as it's necessary to | |
318 // properly reassociate the tab with the entity that was backing it. | |
319 if (synced_tab->IsPlaceholderTab()) { | |
320 // For tabs without WebContents update the |tab_id| and |window_id|, | |
321 // as it could have changed after a session restore. | |
322 if (synced_tab->GetSyncId() > TabNodePool::kInvalidTabNodeID) { | |
323 AssociateRestoredPlaceholderTab(*synced_tab, tab_id, window_id, | |
324 change_output); | |
325 } | |
326 } else if (RELOAD_TABS == option) { | |
327 AssociateTab(synced_tab, change_output); | 281 AssociateTab(synced_tab, change_output); |
328 } | |
329 | 282 |
330 // If the tab was syncable, it would have been added to the tracker | 283 // If the tab is valid, it would have been added to the tracker either |
331 // either by the above Associate[RestoredPlaceholder]Tab call or by the | 284 // by the above AssociateTab call (at association time), or by the |
332 // OnLocalTabModified method invoking AssociateTab directly. Therefore, | 285 // change processor calling AssociateTab for all modified tabs. |
333 // we can key whether this window has valid tabs based on the tab's | 286 // Therefore, we can key whether this window has valid tabs based on |
334 // presence in the tracker. | 287 // the tab's presence in the tracker. |
335 const sessions::SessionTab* tab = nullptr; | 288 const sessions::SessionTab* tab = nullptr; |
336 if (session_tracker_.LookupSessionTab(local_tag, tab_id, &tab)) { | 289 if (session_tracker_.LookupSessionTab(local_tag, tab_id, &tab)) { |
337 found_tabs = true; | 290 found_tabs = true; |
338 window_s.add_tab(tab_id); | 291 window_s.add_tab(tab_id); |
339 } | 292 } |
340 } | 293 } |
341 if (found_tabs) { | 294 if (found_tabs) { |
342 sync_pb::SessionWindow* header_window = header_s->add_window(); | 295 sync_pb::SessionWindow* header_window = header_s->add_window(); |
343 *header_window = window_s; | 296 *header_window = window_s; |
344 | 297 |
345 // Update this window's representation in the synced session tracker. | 298 // Update this window's representation in the synced session tracker. |
346 session_tracker_.PutWindowInSession(local_tag, window_id); | 299 session_tracker_.PutWindowInSession(local_tag, window_id); |
347 BuildSyncedSessionFromSpecifics( | 300 BuildSyncedSessionFromSpecifics( |
348 local_tag, window_s, current_session->modified_time, | 301 local_tag, window_s, current_session->modified_time, |
349 current_session->windows[window_id].get()); | 302 current_session->windows[window_id].get()); |
350 } | 303 } |
351 } | 304 } |
352 } | 305 } |
353 std::set<int> deleted_tab_node_ids; | 306 local_tab_pool_.DeleteUnassociatedTabNodes(change_output); |
354 session_tracker_.CleanupLocalTabs(&deleted_tab_node_ids); | 307 session_tracker_.CleanupSession(local_tag); |
355 AppendDeletionsForTabNodes(deleted_tab_node_ids, current_machine_tag(), | |
356 change_output); | |
357 | 308 |
358 // Always update the header. Sync takes care of dropping this update | 309 // Always update the header. Sync takes care of dropping this update |
359 // if the entity specifics are identical (i.e windows, client name did | 310 // if the entity specifics are identical (i.e windows, client name did |
360 // not change). | 311 // not change). |
361 sync_pb::EntitySpecifics entity; | 312 sync_pb::EntitySpecifics entity; |
362 entity.mutable_session()->CopyFrom(specifics); | 313 entity.mutable_session()->CopyFrom(specifics); |
363 syncer::SyncData data = syncer::SyncData::CreateLocalData( | 314 syncer::SyncData data = syncer::SyncData::CreateLocalData( |
364 current_machine_tag(), current_session_name_, entity); | 315 current_machine_tag(), current_session_name_, entity); |
365 change_output->push_back( | 316 change_output->push_back( |
366 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data)); | 317 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data)); |
367 } | 318 } |
368 | 319 |
369 void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab_delegate, | 320 void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab, |
370 syncer::SyncChangeList* change_output) { | 321 syncer::SyncChangeList* change_output) { |
371 DCHECK(!tab_delegate->IsPlaceholderTab()); | 322 DCHECK(!tab->IsPlaceholderTab()); |
| 323 SessionID::id_type tab_id = tab->GetSessionId(); |
372 | 324 |
373 if (tab_delegate->IsBeingDestroyed()) { | 325 if (tab->IsBeingDestroyed()) { |
374 // Do nothing. By not proactively adding the tab to the session, it will be | 326 // This tab is closing. |
375 // removed if necessary during subsequent cleanup. | 327 TabLinksMap::iterator tab_iter = local_tab_map_.find(tab_id); |
| 328 if (tab_iter == local_tab_map_.end()) { |
| 329 // We aren't tracking this tab (for example, sync setting page). |
| 330 return; |
| 331 } |
| 332 local_tab_pool_.FreeTabNode(tab_iter->second->tab_node_id(), change_output); |
| 333 local_tab_map_.erase(tab_iter); |
376 return; | 334 return; |
377 } | 335 } |
378 | 336 |
379 if (!tab_delegate->ShouldSync(sessions_client_)) | 337 if (!tab->ShouldSync(sessions_client_)) |
380 return; | 338 return; |
381 | 339 |
382 SessionID::id_type tab_id = tab_delegate->GetSessionId(); | 340 TabLinksMap::iterator local_tab_map_iter = local_tab_map_.find(tab_id); |
383 DVLOG(1) << "Syncing tab " << tab_id << " from window " | 341 TabLink* tab_link = nullptr; |
384 << tab_delegate->GetWindowId(); | |
385 | 342 |
386 int tab_node_id = TabNodePool::kInvalidTabNodeID; | 343 if (local_tab_map_iter == local_tab_map_.end()) { |
387 bool existing_tab_node = | 344 int tab_node_id = tab->GetSyncId(); |
388 session_tracker_.GetTabNodeFromLocalTabId(tab_id, &tab_node_id); | 345 // If there is an old sync node for the tab, reuse it. If this is a new |
389 CHECK_NE(TabNodePool::kInvalidTabNodeID, tab_node_id) << "crbug.com/673618"; | 346 // tab, get a sync node for it. |
390 tab_delegate->SetSyncId(tab_node_id); | 347 if (!local_tab_pool_.IsUnassociatedTabNode(tab_node_id)) { |
391 sessions::SessionTab* session_tab = | 348 tab_node_id = local_tab_pool_.GetFreeTabNode(change_output); |
392 session_tracker_.GetTab(current_machine_tag(), tab_id); | 349 tab->SetSyncId(tab_node_id); |
| 350 } |
| 351 local_tab_pool_.AssociateTabNode(tab_node_id, tab_id); |
| 352 tab_link = new TabLink(tab_node_id, tab); |
| 353 local_tab_map_[tab_id] = make_linked_ptr<TabLink>(tab_link); |
| 354 } else { |
| 355 // This tab is already associated with a sync node, reuse it. |
| 356 // Note: on some platforms the tab object may have changed, so we ensure |
| 357 // the tab link is up to date. |
| 358 tab_link = local_tab_map_iter->second.get(); |
| 359 local_tab_map_iter->second->set_tab(tab); |
| 360 } |
| 361 DCHECK(tab_link); |
| 362 DCHECK_NE(tab_link->tab_node_id(), TabNodePool::kInvalidTabNodeID); |
| 363 DVLOG(1) << "Reloading tab " << tab_id << " from window " |
| 364 << tab->GetWindowId(); |
393 | 365 |
394 // Get the previously synced url. | 366 // Write to sync model. |
395 int old_index = session_tab->normalized_navigation_index(); | 367 sync_pb::EntitySpecifics specifics; |
396 GURL old_url; | 368 LocalTabDelegateToSpecifics(*tab, specifics.mutable_session()); |
397 if (session_tab->navigations.size() > static_cast<size_t>(old_index)) | 369 syncer::SyncData data = syncer::SyncData::CreateLocalData( |
398 old_url = session_tab->navigations[old_index].virtual_url(); | 370 TabNodePool::TabIdToTag(current_machine_tag_, tab_link->tab_node_id()), |
| 371 current_session_name_, specifics); |
| 372 change_output->push_back( |
| 373 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data)); |
399 | 374 |
400 // Update the tracker's session representation. | 375 int current_index = tab->GetCurrentEntryIndex(); |
401 SetSessionTabFromDelegate(*tab_delegate, base::Time::Now(), session_tab); | 376 const GURL new_url = tab->GetVirtualURLAtIndex(current_index); |
402 SetVariationIds(session_tab); | 377 if (new_url != tab_link->url()) { |
| 378 tab_link->set_url(new_url); |
| 379 favicon_cache_.OnFaviconVisited(new_url, |
| 380 tab->GetFaviconURLAtIndex(current_index)); |
| 381 page_revisit_broadcaster_.OnPageVisit( |
| 382 new_url, tab->GetTransitionAtIndex(current_index)); |
| 383 } |
| 384 |
403 session_tracker_.GetSession(current_machine_tag())->modified_time = | 385 session_tracker_.GetSession(current_machine_tag())->modified_time = |
404 base::Time::Now(); | 386 base::Time::Now(); |
405 | |
406 // Write to the sync model itself. | |
407 sync_pb::EntitySpecifics specifics; | |
408 specifics.mutable_session()->CopyFrom( | |
409 SessionTabToSpecifics(*session_tab, current_machine_tag(), tab_node_id)); | |
410 syncer::SyncData data = syncer::SyncData::CreateLocalData( | |
411 TabNodeIdToTag(current_machine_tag(), tab_node_id), current_session_name_, | |
412 specifics); | |
413 change_output->push_back(syncer::SyncChange( | |
414 FROM_HERE, existing_tab_node ? syncer::SyncChange::ACTION_UPDATE | |
415 : syncer::SyncChange::ACTION_ADD, | |
416 data)); | |
417 | |
418 int current_index = tab_delegate->GetCurrentEntryIndex(); | |
419 const GURL new_url = tab_delegate->GetVirtualURLAtIndex(current_index); | |
420 if (new_url != old_url) { | |
421 favicon_cache_.OnFaviconVisited( | |
422 new_url, tab_delegate->GetFaviconURLAtIndex(current_index)); | |
423 page_revisit_broadcaster_.OnPageVisit( | |
424 new_url, tab_delegate->GetTransitionAtIndex(current_index)); | |
425 } | |
426 } | 387 } |
427 | 388 |
428 bool SessionsSyncManager::RebuildAssociations() { | 389 bool SessionsSyncManager::RebuildAssociations() { |
429 syncer::SyncDataList data(sync_processor_->GetAllSyncData(syncer::SESSIONS)); | 390 syncer::SyncDataList data(sync_processor_->GetAllSyncData(syncer::SESSIONS)); |
430 std::unique_ptr<syncer::SyncErrorFactory> error_handler( | 391 std::unique_ptr<syncer::SyncErrorFactory> error_handler( |
431 std::move(error_handler_)); | 392 std::move(error_handler_)); |
432 std::unique_ptr<syncer::SyncChangeProcessor> processor( | 393 std::unique_ptr<syncer::SyncChangeProcessor> processor( |
433 std::move(sync_processor_)); | 394 std::move(sync_processor_)); |
434 | 395 |
435 StopSyncing(syncer::SESSIONS); | 396 StopSyncing(syncer::SESSIONS); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
475 DCHECK(!rebuild_association_succeeded || !local_tab_pool_out_of_sync_); | 436 DCHECK(!rebuild_association_succeeded || !local_tab_pool_out_of_sync_); |
476 return; | 437 return; |
477 } | 438 } |
478 | 439 |
479 syncer::SyncChangeList changes; | 440 syncer::SyncChangeList changes; |
480 AssociateTab(modified_tab, &changes); | 441 AssociateTab(modified_tab, &changes); |
481 // Note, we always associate windows because it's possible a tab became | 442 // Note, we always associate windows because it's possible a tab became |
482 // "interesting" by going to a valid URL, in which case it needs to be added | 443 // "interesting" by going to a valid URL, in which case it needs to be added |
483 // to the window's tab information. Similarly, if a tab became | 444 // to the window's tab information. Similarly, if a tab became |
484 // "uninteresting", we remove it from the window's tab information. | 445 // "uninteresting", we remove it from the window's tab information. |
485 AssociateWindows(DONT_RELOAD_TABS, &changes); | 446 AssociateWindows(DONT_RELOAD_TABS, syncer::SyncDataList(), &changes); |
486 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); | 447 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
487 } | 448 } |
488 | 449 |
489 void SessionsSyncManager::OnFaviconsChanged(const std::set<GURL>& page_urls, | 450 void SessionsSyncManager::OnFaviconsChanged(const std::set<GURL>& page_urls, |
490 const GURL& /* icon_url */) { | 451 const GURL& /* icon_url */) { |
491 for (const GURL& page_url : page_urls) | 452 // TODO(zea): consider a separate container for tabs with outstanding favicon |
492 favicon_cache_.OnPageFaviconUpdated(page_url); | 453 // loads so we don't have to iterate through all tabs comparing urls. |
| 454 for (const GURL& page_url : page_urls) { |
| 455 for (TabLinksMap::iterator tab_iter = local_tab_map_.begin(); |
| 456 tab_iter != local_tab_map_.end(); ++tab_iter) { |
| 457 if (tab_iter->second->url() == page_url) |
| 458 favicon_cache_.OnPageFaviconUpdated(page_url); |
| 459 } |
| 460 } |
493 } | 461 } |
494 | 462 |
495 void SessionsSyncManager::StopSyncing(syncer::ModelType type) { | 463 void SessionsSyncManager::StopSyncing(syncer::ModelType type) { |
496 local_event_router_->Stop(); | 464 local_event_router_->Stop(); |
497 if (sync_processor_.get() && lost_navigations_recorder_.get()) { | 465 if (sync_processor_.get() && lost_navigations_recorder_.get()) { |
498 sync_processor_->RemoveLocalChangeObserver( | 466 sync_processor_->RemoveLocalChangeObserver( |
499 lost_navigations_recorder_.get()); | 467 lost_navigations_recorder_.get()); |
500 lost_navigations_recorder_.reset(); | 468 lost_navigations_recorder_.reset(); |
501 } | 469 } |
502 sync_processor_.reset(nullptr); | 470 sync_processor_.reset(nullptr); |
503 error_handler_.reset(); | 471 error_handler_.reset(); |
504 session_tracker_.Clear(); | 472 session_tracker_.Clear(); |
| 473 local_tab_map_.clear(); |
| 474 local_tab_pool_.Clear(); |
505 current_machine_tag_.clear(); | 475 current_machine_tag_.clear(); |
506 current_session_name_.clear(); | 476 current_session_name_.clear(); |
507 local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID; | 477 local_session_header_node_id_ = TabNodePool::kInvalidTabNodeID; |
508 } | 478 } |
509 | 479 |
510 syncer::SyncDataList SessionsSyncManager::GetAllSyncData( | 480 syncer::SyncDataList SessionsSyncManager::GetAllSyncData( |
511 syncer::ModelType type) const { | 481 syncer::ModelType type) const { |
512 syncer::SyncDataList list; | 482 syncer::SyncDataList list; |
513 const SyncedSession* session = nullptr; | 483 const SyncedSession* session = nullptr; |
514 if (!session_tracker_.LookupLocalSession(&session)) | 484 if (!session_tracker_.LookupLocalSession(&session)) |
515 return syncer::SyncDataList(); | 485 return syncer::SyncDataList(); |
516 | 486 |
517 // First construct the header node. | 487 // First construct the header node. |
518 sync_pb::EntitySpecifics header_entity; | 488 sync_pb::EntitySpecifics header_entity; |
519 header_entity.mutable_session()->set_session_tag(current_machine_tag()); | 489 header_entity.mutable_session()->set_session_tag(current_machine_tag()); |
520 sync_pb::SessionHeader* header_specifics = | 490 sync_pb::SessionHeader* header_specifics = |
521 header_entity.mutable_session()->mutable_header(); | 491 header_entity.mutable_session()->mutable_header(); |
522 header_specifics->MergeFrom(session->ToSessionHeader()); | 492 header_specifics->MergeFrom(session->ToSessionHeader()); |
523 syncer::SyncData data = syncer::SyncData::CreateLocalData( | 493 syncer::SyncData data = syncer::SyncData::CreateLocalData( |
524 current_machine_tag(), current_session_name_, header_entity); | 494 current_machine_tag(), current_session_name_, header_entity); |
525 list.push_back(data); | 495 list.push_back(data); |
526 | 496 |
527 for (auto& win_iter : session->windows) { | 497 for (auto win_iter = session->windows.begin(); |
528 for (auto& tab : win_iter.second->tabs) { | 498 win_iter != session->windows.end(); ++win_iter) { |
529 // TODO(zea): replace with with the correct tab node id once there's a | 499 for (auto tabs_iter = win_iter->second->tabs.begin(); |
530 // sync specific wrapper for SessionTab. This method is only used in | 500 tabs_iter != win_iter->second->tabs.end(); ++tabs_iter) { |
531 // tests though, so it's fine for now. crbug.com/662597 | |
532 int tab_node_id = 0; | |
533 sync_pb::EntitySpecifics entity; | 501 sync_pb::EntitySpecifics entity; |
534 entity.mutable_session()->CopyFrom( | 502 sync_pb::SessionSpecifics* specifics = entity.mutable_session(); |
535 SessionTabToSpecifics(*tab, current_machine_tag(), tab_node_id)); | 503 specifics->mutable_tab()->MergeFrom((*tabs_iter)->ToSyncData()); |
| 504 specifics->set_session_tag(current_machine_tag_); |
| 505 |
| 506 TabLinksMap::const_iterator tab_map_iter = |
| 507 local_tab_map_.find((*tabs_iter)->tab_id.id()); |
| 508 DCHECK(tab_map_iter != local_tab_map_.end()); |
| 509 specifics->set_tab_node_id(tab_map_iter->second->tab_node_id()); |
536 syncer::SyncData data = syncer::SyncData::CreateLocalData( | 510 syncer::SyncData data = syncer::SyncData::CreateLocalData( |
537 TabNodeIdToTag(current_machine_tag(), tab_node_id), | 511 TabNodePool::TabIdToTag(current_machine_tag_, |
| 512 specifics->tab_node_id()), |
538 current_session_name_, entity); | 513 current_session_name_, entity); |
539 list.push_back(data); | 514 list.push_back(data); |
540 } | 515 } |
541 } | 516 } |
542 return list; | 517 return list; |
543 } | 518 } |
544 | 519 |
545 bool SessionsSyncManager::GetLocalSession(const SyncedSession** local_session) { | 520 bool SessionsSyncManager::GetLocalSession(const SyncedSession** local_session) { |
546 if (current_machine_tag().empty()) | 521 if (current_machine_tag_.empty()) |
547 return false; | 522 return false; |
548 *local_session = session_tracker_.GetSession(current_machine_tag()); | 523 *local_session = session_tracker_.GetSession(current_machine_tag()); |
549 return true; | 524 return true; |
550 } | 525 } |
551 | 526 |
552 syncer::SyncError SessionsSyncManager::ProcessSyncChanges( | 527 syncer::SyncError SessionsSyncManager::ProcessSyncChanges( |
553 const tracked_objects::Location& from_here, | 528 const tracked_objects::Location& from_here, |
554 const syncer::SyncChangeList& change_list) { | 529 const syncer::SyncChangeList& change_list) { |
555 if (!sync_processor_.get()) { | 530 if (!sync_processor_.get()) { |
556 syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, | 531 syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
596 break; | 571 break; |
597 case syncer::SyncChange::ACTION_ADD: | 572 case syncer::SyncChange::ACTION_ADD: |
598 case syncer::SyncChange::ACTION_UPDATE: | 573 case syncer::SyncChange::ACTION_UPDATE: |
599 if (current_machine_tag() == session.session_tag()) { | 574 if (current_machine_tag() == session.session_tag()) { |
600 // We should only ever receive a change to our own machine's session | 575 // We should only ever receive a change to our own machine's session |
601 // info if encryption was turned on. In that case, the data is still | 576 // info if encryption was turned on. In that case, the data is still |
602 // the same, so we can ignore. | 577 // the same, so we can ignore. |
603 LOG(WARNING) << "Dropping modification to local session."; | 578 LOG(WARNING) << "Dropping modification to local session."; |
604 return syncer::SyncError(); | 579 return syncer::SyncError(); |
605 } | 580 } |
606 UpdateTrackerWithSpecifics( | 581 UpdateTrackerWithForeignSession( |
607 session, syncer::SyncDataRemote(it->sync_data()).GetModifiedTime()); | 582 session, syncer::SyncDataRemote(it->sync_data()).GetModifiedTime()); |
608 break; | 583 break; |
609 default: | 584 default: |
610 NOTREACHED() << "Processing sync changes failed, unknown change type."; | 585 NOTREACHED() << "Processing sync changes failed, unknown change type."; |
611 } | 586 } |
612 } | 587 } |
613 | 588 |
614 if (!sessions_updated_callback_.is_null()) | 589 if (!sessions_updated_callback_.is_null()) |
615 sessions_updated_callback_.Run(); | 590 sessions_updated_callback_.Run(); |
616 return syncer::SyncError(); | 591 return syncer::SyncError(); |
617 } | 592 } |
618 | 593 |
619 syncer::SyncChange SessionsSyncManager::TombstoneTab( | 594 syncer::SyncChange SessionsSyncManager::TombstoneTab( |
620 const sync_pb::SessionSpecifics& tab) { | 595 const sync_pb::SessionSpecifics& tab) { |
621 if (!tab.has_tab_node_id()) { | 596 if (!tab.has_tab_node_id()) { |
622 LOG(WARNING) << "Old sessions node without tab node id; can't tombstone."; | 597 LOG(WARNING) << "Old sessions node without tab node id; can't tombstone."; |
623 return syncer::SyncChange(); | 598 return syncer::SyncChange(); |
624 } else { | 599 } else { |
625 return syncer::SyncChange( | 600 return syncer::SyncChange( |
626 FROM_HERE, SyncChange::ACTION_DELETE, | 601 FROM_HERE, SyncChange::ACTION_DELETE, |
627 SyncData::CreateLocalDelete( | 602 SyncData::CreateLocalDelete( |
628 TabNodeIdToTag(current_machine_tag(), tab.tab_node_id()), | 603 TabNodePool::TabIdToTag(current_machine_tag(), tab.tab_node_id()), |
629 syncer::SESSIONS)); | 604 syncer::SESSIONS)); |
630 } | 605 } |
631 } | 606 } |
632 | 607 |
633 bool SessionsSyncManager::GetAllForeignSessions( | 608 bool SessionsSyncManager::GetAllForeignSessions( |
634 std::vector<const SyncedSession*>* sessions) { | 609 std::vector<const SyncedSession*>* sessions) { |
635 if (!session_tracker_.LookupAllForeignSessions( | 610 if (!session_tracker_.LookupAllForeignSessions( |
636 sessions, SyncedSessionTracker::PRESENTABLE)) | 611 sessions, SyncedSessionTracker::PRESENTABLE)) |
637 return false; | 612 return false; |
638 std::sort(sessions->begin(), sessions->end(), SessionsRecencyComparator); | 613 std::sort(sessions->begin(), sessions->end(), SessionsRecencyComparator); |
639 return true; | 614 return true; |
640 } | 615 } |
641 | 616 |
642 bool SessionsSyncManager::InitFromSyncModel( | 617 bool SessionsSyncManager::InitFromSyncModel( |
643 const syncer::SyncDataList& sync_data, | 618 const syncer::SyncDataList& sync_data, |
| 619 syncer::SyncDataList* restored_tabs, |
644 syncer::SyncChangeList* new_changes) { | 620 syncer::SyncChangeList* new_changes) { |
645 bool found_current_header = false; | 621 bool found_current_header = false; |
646 int bad_foreign_hash_count = 0; | 622 int bad_foreign_hash_count = 0; |
647 for (syncer::SyncDataList::const_iterator it = sync_data.begin(); | 623 for (syncer::SyncDataList::const_iterator it = sync_data.begin(); |
648 it != sync_data.end(); ++it) { | 624 it != sync_data.end(); ++it) { |
649 const syncer::SyncData& data = *it; | 625 const syncer::SyncData& data = *it; |
650 DCHECK(data.GetSpecifics().has_session()); | 626 DCHECK(data.GetSpecifics().has_session()); |
651 syncer::SyncDataRemote remote(data); | 627 syncer::SyncDataRemote remote(data); |
652 const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session(); | 628 const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session(); |
653 if (specifics.session_tag().empty() || | 629 if (specifics.session_tag().empty() || |
654 (specifics.has_tab() && | 630 (specifics.has_tab() && |
655 (!specifics.has_tab_node_id() || !specifics.tab().has_tab_id()))) { | 631 (!specifics.has_tab_node_id() || !specifics.tab().has_tab_id()))) { |
656 syncer::SyncChange tombstone(TombstoneTab(specifics)); | 632 syncer::SyncChange tombstone(TombstoneTab(specifics)); |
657 if (tombstone.IsValid()) | 633 if (tombstone.IsValid()) |
658 new_changes->push_back(tombstone); | 634 new_changes->push_back(tombstone); |
659 } else if (specifics.session_tag() != current_machine_tag()) { | 635 } else if (specifics.session_tag() != current_machine_tag()) { |
660 if (TagHashFromSpecifics(specifics) == remote.GetClientTagHash()) { | 636 if (TagHashFromSpecifics(specifics) == remote.GetClientTagHash()) { |
661 UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime()); | 637 UpdateTrackerWithForeignSession(specifics, remote.GetModifiedTime()); |
662 } else { | 638 } else { |
663 // In the past, like years ago, we believe that some session data was | 639 // In the past, like years ago, we believe that some session data was |
664 // created with bad tag hashes. This causes any change this client makes | 640 // created with bad tag hashes. This causes any change this client makes |
665 // to that foreign data (like deletion through garbage collection) to | 641 // to that foreign data (like deletion through garbage collection) to |
666 // trigger a data type error because the tag looking mechanism fails. So | 642 // trigger a data type error because the tag looking mechanism fails. So |
667 // look for these and delete via remote SyncData, which uses a server id | 643 // look for these and delete via remote SyncData, which uses a server id |
668 // lookup mechanism instead, see crbug.com/604657. | 644 // lookup mechanism instead, see crbug.com/604657. |
669 bad_foreign_hash_count++; | 645 bad_foreign_hash_count++; |
670 new_changes->push_back( | 646 new_changes->push_back( |
671 syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, remote)); | 647 syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, remote)); |
672 } | 648 } |
673 } else { | 649 } else { |
674 // This is previously stored local session information. | 650 // This is previously stored local session information. |
675 if (specifics.has_header() && !found_current_header) { | 651 if (specifics.has_header() && !found_current_header) { |
676 // This is our previous header node, reuse it. | 652 // This is our previous header node, reuse it. |
677 found_current_header = true; | 653 found_current_header = true; |
678 if (specifics.header().has_client_name()) | 654 if (specifics.header().has_client_name()) |
679 current_session_name_ = specifics.header().client_name(); | 655 current_session_name_ = specifics.header().client_name(); |
680 | |
681 // TODO(zea): crbug.com/639009 update the tracker with the specifics | |
682 // from the header node as well. This will be necessary to preserve | |
683 // the set of open tabs when a custom tab is opened. | |
684 } else { | 656 } else { |
685 if (specifics.has_header() || !specifics.has_tab()) { | 657 if (specifics.has_header() || !specifics.has_tab()) { |
686 LOG(WARNING) << "Found more than one session header node with local " | 658 LOG(WARNING) << "Found more than one session header node with local " |
687 << "tag."; | 659 << "tag."; |
688 syncer::SyncChange tombstone(TombstoneTab(specifics)); | 660 syncer::SyncChange tombstone(TombstoneTab(specifics)); |
689 if (tombstone.IsValid()) | 661 if (tombstone.IsValid()) |
690 new_changes->push_back(tombstone); | 662 new_changes->push_back(tombstone); |
691 } else if (specifics.tab().tab_id() == TabNodePool::kInvalidTabID) { | |
692 LOG(WARNING) << "Found tab node with invalid tab id."; | |
693 syncer::SyncChange tombstone(TombstoneTab(specifics)); | |
694 if (tombstone.IsValid()) | |
695 new_changes->push_back(tombstone); | |
696 } else { | 663 } else { |
697 // This is a valid old tab node, add it to the tracker and associate | 664 // This is a valid old tab node, add it to the pool so it can be |
698 // it. | 665 // reused for reassociation. |
699 DVLOG(1) << "Associating local tab " << specifics.tab().tab_id() | 666 local_tab_pool_.AddTabNode(specifics.tab_node_id()); |
700 << " with node " << specifics.tab_node_id(); | 667 restored_tabs->push_back(*it); |
701 session_tracker_.ReassociateLocalTab(specifics.tab_node_id(), | |
702 specifics.tab().tab_id()); | |
703 UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime()); | |
704 } | 668 } |
705 } | 669 } |
706 } | 670 } |
707 } | 671 } |
708 | 672 |
709 // Cleanup all foreign sessions, since orphaned tabs may have been added after | 673 // Cleanup all foreign sessions, since orphaned tabs may have been added after |
710 // the header. | 674 // the header. |
711 std::vector<const SyncedSession*> sessions; | 675 std::vector<const SyncedSession*> sessions; |
712 session_tracker_.LookupAllForeignSessions(&sessions, | 676 session_tracker_.LookupAllForeignSessions(&sessions, |
713 SyncedSessionTracker::RAW); | 677 SyncedSessionTracker::RAW); |
714 for (const auto* session : sessions) { | 678 for (const auto* session : sessions) { |
715 session_tracker_.CleanupForeignSession(session->session_tag); | 679 session_tracker_.CleanupSession(session->session_tag); |
716 } | 680 } |
717 | 681 |
718 UMA_HISTOGRAM_COUNTS_100("Sync.SessionsBadForeignHashOnMergeCount", | 682 UMA_HISTOGRAM_COUNTS_100("Sync.SessionsBadForeignHashOnMergeCount", |
719 bad_foreign_hash_count); | 683 bad_foreign_hash_count); |
720 | 684 |
721 return found_current_header; | 685 return found_current_header; |
722 } | 686 } |
723 | 687 |
724 void SessionsSyncManager::UpdateTrackerWithSpecifics( | 688 void SessionsSyncManager::UpdateTrackerWithForeignSession( |
725 const sync_pb::SessionSpecifics& specifics, | 689 const sync_pb::SessionSpecifics& specifics, |
726 const base::Time& modification_time) { | 690 const base::Time& modification_time) { |
727 std::string session_tag = specifics.session_tag(); | 691 std::string foreign_session_tag = specifics.session_tag(); |
728 SyncedSession* session = session_tracker_.GetSession(session_tag); | 692 DCHECK_NE(foreign_session_tag, current_machine_tag()); |
| 693 |
| 694 SyncedSession* foreign_session = |
| 695 session_tracker_.GetSession(foreign_session_tag); |
729 if (specifics.has_header()) { | 696 if (specifics.has_header()) { |
730 // Read in the header data for this session. Header data is | 697 // Read in the header data for this foreign session. Header data is |
731 // essentially a collection of windows, each of which has an ordered id list | 698 // essentially a collection of windows, each of which has an ordered id list |
732 // for their tabs. | 699 // for their tabs. |
733 | 700 |
734 if (!IsValidSessionHeader(specifics.header())) { | 701 if (!IsValidSessionHeader(specifics.header())) { |
735 LOG(WARNING) << "Ignoring session node with invalid header " | 702 LOG(WARNING) << "Ignoring foreign session node with invalid header " |
736 << "and tag " << session_tag << "."; | 703 << "and tag " << foreign_session_tag << "."; |
737 return; | 704 return; |
738 } | 705 } |
739 | 706 |
740 // Load (or create) the SyncedSession object for this client. | 707 // Load (or create) the SyncedSession object for this client. |
741 const sync_pb::SessionHeader& header = specifics.header(); | 708 const sync_pb::SessionHeader& header = specifics.header(); |
742 PopulateSessionHeaderFromSpecifics(header, modification_time, session); | 709 PopulateSessionHeaderFromSpecifics(header, modification_time, |
| 710 foreign_session); |
743 | 711 |
744 // Reset the tab/window tracking for this session (must do this before | 712 // Reset the tab/window tracking for this session (must do this before |
745 // we start calling PutWindowInSession and PutTabInWindow so that all | 713 // we start calling PutWindowInSession and PutTabInWindow so that all |
746 // unused tabs/windows get cleared by the CleanupSession(...) call). | 714 // unused tabs/windows get cleared by the CleanupSession(...) call). |
747 session_tracker_.ResetSessionTracking(session_tag); | 715 session_tracker_.ResetSessionTracking(foreign_session_tag); |
748 | 716 |
749 // Process all the windows and their tab information. | 717 // Process all the windows and their tab information. |
750 int num_windows = header.window_size(); | 718 int num_windows = header.window_size(); |
751 DVLOG(1) << "Populating " << session_tag << " with " << num_windows | 719 DVLOG(1) << "Associating " << foreign_session_tag << " with " << num_windows |
752 << " windows."; | 720 << " windows."; |
753 | 721 |
754 for (int i = 0; i < num_windows; ++i) { | 722 for (int i = 0; i < num_windows; ++i) { |
755 const sync_pb::SessionWindow& window_s = header.window(i); | 723 const sync_pb::SessionWindow& window_s = header.window(i); |
756 SessionID::id_type window_id = window_s.window_id(); | 724 SessionID::id_type window_id = window_s.window_id(); |
757 session_tracker_.PutWindowInSession(session_tag, window_id); | 725 session_tracker_.PutWindowInSession(foreign_session_tag, window_id); |
758 BuildSyncedSessionFromSpecifics(session_tag, window_s, modification_time, | 726 BuildSyncedSessionFromSpecifics( |
759 session->windows[window_id].get()); | 727 foreign_session_tag, window_s, modification_time, |
| 728 foreign_session->windows[window_id].get()); |
760 } | 729 } |
761 // Delete any closed windows and unused tabs as necessary. | 730 // Delete any closed windows and unused tabs as necessary. |
762 session_tracker_.CleanupForeignSession(session_tag); | 731 session_tracker_.CleanupSession(foreign_session_tag); |
763 } else if (specifics.has_tab()) { | 732 } else if (specifics.has_tab()) { |
764 const sync_pb::SessionTab& tab_s = specifics.tab(); | 733 const sync_pb::SessionTab& tab_s = specifics.tab(); |
765 SessionID::id_type tab_id = tab_s.tab_id(); | 734 SessionID::id_type tab_id = tab_s.tab_id(); |
766 DVLOG(1) << "Populating " << session_tag << "'s tab id " << tab_id | |
767 << " from node " << specifics.tab_node_id(); | |
768 | 735 |
769 // Ensure the tracker is aware of the tab node id. Deleting foreign sessions | 736 const sessions::SessionTab* existing_tab; |
770 // requires deleting all relevant tab nodes, and it's easier to track the | 737 if (session_tracker_.LookupSessionTab(foreign_session_tag, tab_id, |
771 // tab node ids themselves separately from the tab ids. | 738 &existing_tab) && |
772 // | 739 existing_tab->timestamp > modification_time) { |
773 // Note that TabIDs are not stable across restarts of a client. Consider | 740 // Force the tracker to remember this tab node id, even if it isn't |
774 // this example with two tabs: | 741 // currently being used. |
775 // | 742 session_tracker_.GetTab(foreign_session_tag, tab_id, |
776 // http://a.com TabID1 --> NodeIDA | 743 specifics.tab_node_id()); |
777 // http://b.com TabID2 --> NodeIDB | 744 DVLOG(1) << "Ignoring " << foreign_session_tag << "'s session tab " |
778 // | 745 << tab_id << " with earlier modification time"; |
779 // After restart, tab ids are reallocated. e.g, one possibility: | |
780 // http://a.com TabID2 --> NodeIDA | |
781 // http://b.com TabID1 --> NodeIDB | |
782 // | |
783 // If that happened on a remote client, here we will see an update to | |
784 // TabID1 with tab_node_id changing from NodeIDA to NodeIDB, and TabID2 | |
785 // with tab_node_id changing from NodeIDB to NodeIDA. | |
786 // | |
787 // We can also wind up here if we created this tab as an out-of-order | |
788 // update to the header node for this session before actually associating | |
789 // the tab itself, so the tab node id wasn't available at the time and | |
790 // is currently kInvalidTabNodeID. | |
791 // | |
792 // In both cases, we can safely throw it into the set of node ids. | |
793 session_tracker_.OnTabNodeSeen(session_tag, specifics.tab_node_id()); | |
794 sessions::SessionTab* tab = session_tracker_.GetTab(session_tag, tab_id); | |
795 if (!tab->timestamp.is_null() && tab->timestamp > modification_time) { | |
796 DVLOG(1) << "Ignoring " << session_tag << "'s session tab " << tab_id | |
797 << " with earlier modification time: " << tab->timestamp | |
798 << " vs " << modification_time; | |
799 return; | 746 return; |
800 } | 747 } |
801 | 748 |
| 749 sessions::SessionTab* tab = session_tracker_.GetTab( |
| 750 foreign_session_tag, tab_id, specifics.tab_node_id()); |
| 751 |
802 // Update SessionTab based on protobuf. | 752 // Update SessionTab based on protobuf. |
803 tab->SetFromSyncData(tab_s, modification_time); | 753 tab->SetFromSyncData(tab_s, modification_time); |
804 | 754 |
805 // If a favicon or favicon urls are present, load the URLs and visit | 755 // If a favicon or favicon urls are present, load the URLs and visit |
806 // times into the in-memory favicon cache. | 756 // times into the in-memory favicon cache. |
807 RefreshFaviconVisitTimesFromForeignTab(tab_s, modification_time); | 757 RefreshFaviconVisitTimesFromForeignTab(tab_s, modification_time); |
808 | 758 |
809 // Update the last modified time. | 759 // Update the last modified time. |
810 if (session->modified_time < modification_time) | 760 if (foreign_session->modified_time < modification_time) |
811 session->modified_time = modification_time; | 761 foreign_session->modified_time = modification_time; |
812 } else { | 762 } else { |
813 LOG(WARNING) << "Ignoring session node with missing header/tab " | 763 LOG(WARNING) << "Ignoring foreign session node with missing header/tab " |
814 << "fields and tag " << session_tag << "."; | 764 << "fields and tag " << foreign_session_tag << "."; |
815 } | 765 } |
816 } | 766 } |
817 | 767 |
818 void SessionsSyncManager::InitializeCurrentMachineTag( | 768 void SessionsSyncManager::InitializeCurrentMachineTag( |
819 const std::string& cache_guid) { | 769 const std::string& cache_guid) { |
820 DCHECK(current_machine_tag_.empty()); | 770 DCHECK(current_machine_tag_.empty()); |
821 std::string persisted_guid; | 771 std::string persisted_guid; |
822 persisted_guid = sync_prefs_->GetSyncSessionsGUID(); | 772 persisted_guid = sync_prefs_->GetSyncSessionsGUID(); |
823 if (!persisted_guid.empty()) { | 773 if (!persisted_guid.empty()) { |
824 current_machine_tag_ = persisted_guid; | 774 current_machine_tag_ = persisted_guid; |
825 DVLOG(1) << "Restoring persisted session sync guid: " << persisted_guid; | 775 DVLOG(1) << "Restoring persisted session sync guid: " << persisted_guid; |
826 } else { | 776 } else { |
827 DCHECK(!cache_guid.empty()); | 777 DCHECK(!cache_guid.empty()); |
828 current_machine_tag_ = BuildMachineTag(cache_guid); | 778 current_machine_tag_ = BuildMachineTag(cache_guid); |
829 DVLOG(1) << "Creating session sync guid: " << current_machine_tag_; | 779 DVLOG(1) << "Creating session sync guid: " << current_machine_tag_; |
830 sync_prefs_->SetSyncSessionsGUID(current_machine_tag_); | 780 sync_prefs_->SetSyncSessionsGUID(current_machine_tag_); |
831 } | 781 } |
| 782 |
| 783 local_tab_pool_.SetMachineTag(current_machine_tag_); |
832 } | 784 } |
833 | 785 |
834 // static | 786 // static |
835 void SessionsSyncManager::PopulateSessionHeaderFromSpecifics( | 787 void SessionsSyncManager::PopulateSessionHeaderFromSpecifics( |
836 const sync_pb::SessionHeader& header_specifics, | 788 const sync_pb::SessionHeader& header_specifics, |
837 base::Time mtime, | 789 base::Time mtime, |
838 SyncedSession* session_header) { | 790 SyncedSession* session_header) { |
839 if (header_specifics.has_client_name()) | 791 if (header_specifics.has_client_name()) |
840 session_header->session_name = header_specifics.client_name(); | 792 session_header->session_name = header_specifics.client_name(); |
841 if (header_specifics.has_device_type()) { | 793 if (header_specifics.has_device_type()) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
885 if (specifics.browser_type() == | 837 if (specifics.browser_type() == |
886 sync_pb::SessionWindow_BrowserType_TYPE_TABBED) { | 838 sync_pb::SessionWindow_BrowserType_TYPE_TABBED) { |
887 session_window->type = sessions::SessionWindow::TYPE_TABBED; | 839 session_window->type = sessions::SessionWindow::TYPE_TABBED; |
888 } else { | 840 } else { |
889 // Note: custom tabs are treated like popup windows on restore, as you can | 841 // Note: custom tabs are treated like popup windows on restore, as you can |
890 // restore a custom tab on a platform that doesn't support them. | 842 // restore a custom tab on a platform that doesn't support them. |
891 session_window->type = sessions::SessionWindow::TYPE_POPUP; | 843 session_window->type = sessions::SessionWindow::TYPE_POPUP; |
892 } | 844 } |
893 } | 845 } |
894 session_window->timestamp = mtime; | 846 session_window->timestamp = mtime; |
895 session_window->tabs.clear(); | 847 session_window->tabs.resize(specifics.tab_size()); |
896 for (int i = 0; i < specifics.tab_size(); i++) { | 848 for (int i = 0; i < specifics.tab_size(); i++) { |
897 SessionID::id_type tab_id = specifics.tab(i); | 849 SessionID::id_type tab_id = specifics.tab(i); |
898 session_tracker_.PutTabInWindow(session_tag, session_window->window_id.id(), | 850 session_tracker_.PutTabInWindow(session_tag, session_window->window_id.id(), |
899 tab_id); | 851 tab_id, i); |
900 } | 852 } |
901 } | 853 } |
902 | 854 |
903 void SessionsSyncManager::RefreshFaviconVisitTimesFromForeignTab( | 855 void SessionsSyncManager::RefreshFaviconVisitTimesFromForeignTab( |
904 const sync_pb::SessionTab& tab, | 856 const sync_pb::SessionTab& tab, |
905 const base::Time& modification_time) { | 857 const base::Time& modification_time) { |
906 // First go through and iterate over all the navigations, checking if any | 858 // First go through and iterate over all the navigations, checking if any |
907 // have valid favicon urls. | 859 // have valid favicon urls. |
908 for (int i = 0; i < tab.navigation_size(); ++i) { | 860 for (int i = 0; i < tab.navigation_size(); ++i) { |
909 if (!tab.navigation(i).favicon_url().empty()) { | 861 if (!tab.navigation(i).favicon_url().empty()) { |
(...skipping 21 matching lines...) Expand all Loading... |
931 void SessionsSyncManager::DeleteForeignSessionInternal( | 883 void SessionsSyncManager::DeleteForeignSessionInternal( |
932 const std::string& tag, | 884 const std::string& tag, |
933 syncer::SyncChangeList* change_output) { | 885 syncer::SyncChangeList* change_output) { |
934 if (tag == current_machine_tag()) { | 886 if (tag == current_machine_tag()) { |
935 LOG(ERROR) << "Attempting to delete local session. This is not currently " | 887 LOG(ERROR) << "Attempting to delete local session. This is not currently " |
936 << "supported."; | 888 << "supported."; |
937 return; | 889 return; |
938 } | 890 } |
939 | 891 |
940 std::set<int> tab_node_ids_to_delete; | 892 std::set<int> tab_node_ids_to_delete; |
941 session_tracker_.LookupForeignTabNodeIds(tag, &tab_node_ids_to_delete); | 893 session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete); |
942 if (DisassociateForeignSession(tag)) { | 894 if (DisassociateForeignSession(tag)) { |
943 // Only tell sync to delete the header if there was one. | 895 // Only tell sync to delete the header if there was one. |
944 change_output->push_back( | 896 change_output->push_back( |
945 syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, | 897 syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, |
946 SyncData::CreateLocalDelete(tag, syncer::SESSIONS))); | 898 SyncData::CreateLocalDelete(tag, syncer::SESSIONS))); |
947 } | 899 } |
948 AppendDeletionsForTabNodes(tab_node_ids_to_delete, tag, change_output); | 900 for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin(); |
| 901 it != tab_node_ids_to_delete.end(); ++it) { |
| 902 change_output->push_back(syncer::SyncChange( |
| 903 FROM_HERE, SyncChange::ACTION_DELETE, |
| 904 SyncData::CreateLocalDelete(TabNodePool::TabIdToTag(tag, *it), |
| 905 syncer::SESSIONS))); |
| 906 } |
949 if (!sessions_updated_callback_.is_null()) | 907 if (!sessions_updated_callback_.is_null()) |
950 sessions_updated_callback_.Run(); | 908 sessions_updated_callback_.Run(); |
951 } | 909 } |
952 | 910 |
953 bool SessionsSyncManager::DisassociateForeignSession( | 911 bool SessionsSyncManager::DisassociateForeignSession( |
954 const std::string& foreign_session_tag) { | 912 const std::string& foreign_session_tag) { |
955 DCHECK_NE(foreign_session_tag, current_machine_tag()); | 913 DCHECK_NE(foreign_session_tag, current_machine_tag()); |
956 DVLOG(1) << "Disassociating session " << foreign_session_tag; | 914 DVLOG(1) << "Disassociating session " << foreign_session_tag; |
957 return session_tracker_.DeleteForeignSession(foreign_session_tag); | 915 return session_tracker_.DeleteSession(foreign_session_tag); |
958 } | 916 } |
959 | 917 |
960 bool SessionsSyncManager::GetForeignSession( | 918 bool SessionsSyncManager::GetForeignSession( |
961 const std::string& tag, | 919 const std::string& tag, |
962 std::vector<const sessions::SessionWindow*>* windows) { | 920 std::vector<const sessions::SessionWindow*>* windows) { |
963 return session_tracker_.LookupSessionWindows(tag, windows); | 921 return session_tracker_.LookupSessionWindows(tag, windows); |
964 } | 922 } |
965 | 923 |
966 bool SessionsSyncManager::GetForeignSessionTabs( | 924 bool SessionsSyncManager::GetForeignSessionTabs( |
967 const std::string& tag, | 925 const std::string& tag, |
(...skipping 25 matching lines...) Expand all Loading... |
993 bool SessionsSyncManager::GetForeignTab(const std::string& tag, | 951 bool SessionsSyncManager::GetForeignTab(const std::string& tag, |
994 const SessionID::id_type tab_id, | 952 const SessionID::id_type tab_id, |
995 const sessions::SessionTab** tab) { | 953 const sessions::SessionTab** tab) { |
996 const sessions::SessionTab* synced_tab = nullptr; | 954 const sessions::SessionTab* synced_tab = nullptr; |
997 bool success = session_tracker_.LookupSessionTab(tag, tab_id, &synced_tab); | 955 bool success = session_tracker_.LookupSessionTab(tag, tab_id, &synced_tab); |
998 if (success) | 956 if (success) |
999 *tab = synced_tab; | 957 *tab = synced_tab; |
1000 return success; | 958 return success; |
1001 } | 959 } |
1002 | 960 |
| 961 void SessionsSyncManager::LocalTabDelegateToSpecifics( |
| 962 const SyncedTabDelegate& tab_delegate, |
| 963 sync_pb::SessionSpecifics* specifics) { |
| 964 sessions::SessionTab* session_tab = nullptr; |
| 965 session_tab = session_tracker_.GetTab(current_machine_tag(), |
| 966 tab_delegate.GetSessionId(), |
| 967 tab_delegate.GetSyncId()); |
| 968 SetSessionTabFromDelegate(tab_delegate, base::Time::Now(), session_tab); |
| 969 SetVariationIds(session_tab); |
| 970 sync_pb::SessionTab tab_s = session_tab->ToSyncData(); |
| 971 specifics->set_session_tag(current_machine_tag_); |
| 972 specifics->set_tab_node_id(tab_delegate.GetSyncId()); |
| 973 specifics->mutable_tab()->CopyFrom(tab_s); |
| 974 } |
| 975 |
1003 void SessionsSyncManager::AssociateRestoredPlaceholderTab( | 976 void SessionsSyncManager::AssociateRestoredPlaceholderTab( |
1004 const SyncedTabDelegate& tab_delegate, | 977 const SyncedTabDelegate& tab_delegate, |
1005 SessionID::id_type new_tab_id, | 978 SessionID::id_type new_tab_id, |
1006 SessionID::id_type new_window_id, | 979 SessionID::id_type new_window_id, |
| 980 const syncer::SyncDataList& restored_tabs, |
1007 syncer::SyncChangeList* change_output) { | 981 syncer::SyncChangeList* change_output) { |
1008 DCHECK_NE(tab_delegate.GetSyncId(), TabNodePool::kInvalidTabNodeID); | 982 DCHECK_NE(tab_delegate.GetSyncId(), TabNodePool::kInvalidTabNodeID); |
1009 | 983 // Rewrite the tab using |restored_tabs| to retrieve the specifics. |
1010 // It's possible the placeholder tab is associated with a tab node that's | 984 if (restored_tabs.empty()) { |
1011 // since been deleted. If that's the case, there's no way to reassociate it, | 985 DLOG(WARNING) << "Can't Update tab ID."; |
1012 // so just return now without adding the tab to the session tracker. | |
1013 if (!session_tracker_.IsLocalTabNodeAssociated(tab_delegate.GetSyncId())) { | |
1014 DVLOG(1) << "Restored placeholder tab's node " << tab_delegate.GetSyncId() | |
1015 << " deleted."; | |
1016 return; | 986 return; |
1017 } | 987 } |
1018 | 988 |
1019 // Update tracker with the new association (and inform it of the tab node | 989 for (syncer::SyncDataList::const_iterator it = restored_tabs.begin(); |
1020 // in the process). | 990 it != restored_tabs.end(); ++it) { |
1021 session_tracker_.ReassociateLocalTab(tab_delegate.GetSyncId(), new_tab_id); | 991 if (it->GetSpecifics().session().tab_node_id() != |
| 992 tab_delegate.GetSyncId()) { |
| 993 continue; |
| 994 } |
1022 | 995 |
1023 // Update the window id on the SessionTab itself. | 996 sync_pb::EntitySpecifics entity; |
1024 sessions::SessionTab* local_tab = | 997 sync_pb::SessionSpecifics* specifics = entity.mutable_session(); |
1025 session_tracker_.GetTab(current_machine_tag(), new_tab_id); | 998 specifics->CopyFrom(it->GetSpecifics().session()); |
1026 local_tab->window_id.set_id(new_window_id); | 999 DCHECK(specifics->has_tab()); |
1027 | 1000 |
1028 // Rewrite the specifics based on the reassociated SessionTab to preserve | 1001 // Update tab node pool with the new association. |
1029 // the new tab and window ids. | 1002 local_tab_pool_.ReassociateTabNode(tab_delegate.GetSyncId(), new_tab_id); |
1030 sync_pb::EntitySpecifics entity; | 1003 TabLink* tab_link = new TabLink(tab_delegate.GetSyncId(), &tab_delegate); |
1031 entity.mutable_session()->CopyFrom(SessionTabToSpecifics( | 1004 local_tab_map_[new_tab_id] = make_linked_ptr<TabLink>(tab_link); |
1032 *local_tab, current_machine_tag(), tab_delegate.GetSyncId())); | 1005 |
1033 syncer::SyncData data = syncer::SyncData::CreateLocalData( | 1006 if (specifics->tab().tab_id() == new_tab_id && |
1034 TabNodeIdToTag(current_machine_tag(), tab_delegate.GetSyncId()), | 1007 specifics->tab().window_id() == new_window_id) |
1035 current_session_name_, entity); | 1008 return; |
1036 change_output->push_back( | 1009 |
1037 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data)); | 1010 // Either the tab_id or window_id changed (e.g due to session restore), so |
| 1011 // update the sync node. |
| 1012 specifics->mutable_tab()->set_tab_id(new_tab_id); |
| 1013 specifics->mutable_tab()->set_window_id(new_window_id); |
| 1014 syncer::SyncData data = syncer::SyncData::CreateLocalData( |
| 1015 TabNodePool::TabIdToTag(current_machine_tag_, specifics->tab_node_id()), |
| 1016 current_session_name_, entity); |
| 1017 change_output->push_back( |
| 1018 syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data)); |
| 1019 return; |
| 1020 } |
1038 } | 1021 } |
1039 | 1022 |
1040 // static | 1023 // static |
1041 void SessionsSyncManager::SetSessionTabFromDelegate( | 1024 void SessionsSyncManager::SetSessionTabFromDelegate( |
1042 const SyncedTabDelegate& tab_delegate, | 1025 const SyncedTabDelegate& tab_delegate, |
1043 base::Time mtime, | 1026 base::Time mtime, |
1044 sessions::SessionTab* session_tab) { | 1027 sessions::SessionTab* session_tab) { |
1045 DCHECK(session_tab); | 1028 DCHECK(session_tab); |
1046 session_tab->window_id.set_id(tab_delegate.GetWindowId()); | 1029 session_tab->window_id.set_id(tab_delegate.GetWindowId()); |
1047 session_tab->tab_id.set_id(tab_delegate.GetSessionId()); | 1030 session_tab->tab_id.set_id(tab_delegate.GetSessionId()); |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1146 } | 1129 } |
1147 | 1130 |
1148 // static | 1131 // static |
1149 std::string SessionsSyncManager::TagHashFromSpecifics( | 1132 std::string SessionsSyncManager::TagHashFromSpecifics( |
1150 const sync_pb::SessionSpecifics& specifics) { | 1133 const sync_pb::SessionSpecifics& specifics) { |
1151 return syncer::GenerateSyncableHash(syncer::SESSIONS, | 1134 return syncer::GenerateSyncableHash(syncer::SESSIONS, |
1152 TagFromSpecifics(specifics)); | 1135 TagFromSpecifics(specifics)); |
1153 } | 1136 } |
1154 | 1137 |
1155 }; // namespace sync_sessions | 1138 }; // namespace sync_sessions |
OLD | NEW |