1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/sessions2/sessions_sync_manager.h"
7 #include "chrome/browser/chrome_notification_types.h"
8 #if !defined(OS_ANDROID)
9 #include "chrome/browser/network_time/navigation_time_helper.h"
11 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
12 #include "chrome/browser/sync/glue/synced_window_delegate.h"
13 #include "chrome/common/url_constants.h"
14 #include "content/public/browser/favicon_status.h"
15 #include "content/public/browser/navigation_entry.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/common/url_constants.h"
20 #include "sync/api/sync_error.h"
21 #include "sync/api/sync_error_factory.h"
22 #include "sync/api/sync_merge_result.h"
23 #include "sync/api/time.h"
25 using content::NavigationEntry;
26 using sessions::SerializedNavigationEntry;
27 using syncer::SyncChange;
28 using syncer::SyncData;
30 namespace browser_sync {
32 // Maximum number of favicons to sync.
33 // TODO(zea): pull this from the server.
34 static const int kMaxSyncFavicons = 200;
36 // The maximum number of navigations in each direction we care to sync.
37 static const int kMaxSyncNavigationCount = 6;
39 SessionsSyncManager::SessionsSyncManager(
41 scoped_ptr<SyncPrefs> sync_prefs,
42 SyncInternalApiDelegate* delegate)
43 : favicon_cache_(profile, kMaxSyncFavicons),
46 local_session_header_node_id_(TabNodePool2::kInvalidTabNodeID) {
47 sync_prefs_ = sync_prefs.Pass();
50 SessionsSyncManager::~SessionsSyncManager() {
53 // Returns the GUID-based string that should be used for
54 // |SessionsSyncManager::current_machine_tag_|.
55 static std::string BuildMachineTag(const std::string& cache_guid) {
56 std::string machine_tag = "session_sync";
57 machine_tag.append(cache_guid);
61 syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing(
62 syncer::ModelType type,
63 const syncer::SyncDataList& initial_sync_data,
64 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
65 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
66 syncer::SyncMergeResult merge_result(type);
67 DCHECK(session_tracker_.Empty());
68 DCHECK_EQ(0U, local_tab_pool_.Capacity());
70 error_handler_ = error_handler.Pass();
71 sync_processor_ = sync_processor.Pass();
73 local_session_header_node_id_ = TabNodePool2::kInvalidTabNodeID;
74 scoped_ptr<DeviceInfo> local_device_info(delegate_->GetLocalDeviceInfo());
75 syncer::SyncChangeList new_changes;
77 // Make sure we have a machine tag. We do this now (versus earlier) as it's
78 // a conveniently safe time to assert sync is ready and the cache_guid is
80 if (current_machine_tag_.empty())
81 InitializeCurrentMachineTag();
82 if (local_device_info) {
83 current_session_name_ = local_device_info->client_name();
85 merge_result.set_error(error_handler_->CreateAndUploadError(
87 "Failed to get device info for machine tag."));
90 session_tracker_.SetLocalSessionTag(current_machine_tag_);
92 // First, we iterate over sync data to update our session_tracker_.
93 if (!InitFromSyncModel(initial_sync_data, &new_changes)) {
94 // The sync db didn't have a header node for us. Create one.
95 sync_pb::EntitySpecifics specifics;
96 sync_pb::SessionSpecifics* base_specifics = specifics.mutable_session();
97 base_specifics->set_session_tag(current_machine_tag());
98 sync_pb::SessionHeader* header_s = base_specifics->mutable_header();
99 header_s->set_client_name(current_session_name_);
100 header_s->set_device_type(DeviceInfo::GetLocalDeviceType());
101 syncer::SyncData data = syncer::SyncData::CreateLocalData(
102 current_machine_tag(), current_session_name_, specifics);
103 new_changes.push_back(syncer::SyncChange(
104 FROM_HERE, syncer::SyncChange::ACTION_ADD, data));
107 #if defined(OS_ANDROID)
108 std::string sync_machine_tag(BuildMachineTag(delegate_->GetCacheGuid()));
109 if (current_machine_tag_.compare(sync_machine_tag) != 0)
110 DeleteForeignSession(sync_machine_tag, &new_changes);
113 // Check if anything has changed on the local client side.
114 AssociateWindows(RELOAD_TABS, &new_changes);
116 merge_result.set_error(
117 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
121 void SessionsSyncManager::AssociateWindows(
122 ReloadTabsOption option,
123 syncer::SyncChangeList* change_output) {
124 const std::string local_tag = current_machine_tag();
125 sync_pb::SessionSpecifics specifics;
126 specifics.set_session_tag(local_tag);
127 sync_pb::SessionHeader* header_s = specifics.mutable_header();
128 SyncedSession* current_session = session_tracker_.GetSession(local_tag);
129 current_session->modified_time = base::Time::Now();
130 header_s->set_client_name(current_session_name_);
131 header_s->set_device_type(DeviceInfo::GetLocalDeviceType());
133 session_tracker_.ResetSessionTracking(local_tag);
134 std::set<SyncedWindowDelegate*> windows =
135 SyncedWindowDelegate::GetSyncedWindowDelegates();
137 for (std::set<SyncedWindowDelegate*>::const_iterator i =
138 windows.begin(); i != windows.end(); ++i) {
139 // Make sure the window has tabs and a viewable window. The viewable window
140 // check is necessary because, for example, when a browser is closed the
141 // destructor is not necessarily run immediately. This means its possible
142 // for us to get a handle to a browser that is about to be removed. If
143 // the tab count is 0 or the window is NULL, the browser is about to be
144 // deleted, so we ignore it.
145 if (ShouldSyncWindow(*i) && (*i)->GetTabCount() && (*i)->HasWindow()) {
146 sync_pb::SessionWindow window_s;
147 SessionID::id_type window_id = (*i)->GetSessionId();
148 DVLOG(1) << "Associating window " << window_id << " with "
149 << (*i)->GetTabCount() << " tabs.";
150 window_s.set_window_id(window_id);
151 // Note: We don't bother to set selected tab index anymore. We still
152 // consume it when receiving foreign sessions, as reading it is free, but
153 // it triggers too many sync cycles with too little value to make setting
155 if ((*i)->IsTypeTabbed()) {
156 window_s.set_browser_type(
157 sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
159 window_s.set_browser_type(
160 sync_pb::SessionWindow_BrowserType_TYPE_POPUP);
163 bool found_tabs = false;
164 for (int j = 0; j < (*i)->GetTabCount(); ++j) {
165 SessionID::id_type tab_id = (*i)->GetTabIdAt(j);
166 SyncedTabDelegate* synced_tab = (*i)->GetTabAt(j);
168 // GetTabAt can return a null tab; in that case just skip it.
172 if (!synced_tab->HasWebContents()) {
173 // For tabs without WebContents update the |tab_id|, as it could have
174 // changed after a session restore.
175 // Note: We cannot check if a tab is valid if it has no WebContents.
176 // We assume any such tab is valid and leave the contents of
177 // corresponding sync node unchanged.
178 if (synced_tab->GetSyncId() > TabNodePool2::kInvalidTabNodeID &&
179 tab_id > TabNodePool2::kInvalidTabID) {
180 UpdateTabIdIfNecessary(*synced_tab, tab_id, change_output);
182 window_s.add_tab(tab_id);
187 if (RELOAD_TABS == option)
188 AssociateTab(synced_tab, change_output);
190 // If the tab is valid, it would have been added to the tracker either
191 // by the above AssociateTab call (at association time), or by the
192 // change processor calling AssociateTab for all modified tabs.
193 // Therefore, we can key whether this window has valid tabs based on
194 // the tab's presence in the tracker.
195 const SessionTab* tab = NULL;
196 if (session_tracker_.LookupSessionTab(local_tag, tab_id, &tab)) {
198 window_s.add_tab(tab_id);
202 sync_pb::SessionWindow* header_window = header_s->add_window();
203 *header_window = window_s;
205 // Update this window's representation in the synced session tracker.
206 session_tracker_.PutWindowInSession(local_tag, window_id);
207 BuildSyncedSessionFromSpecifics(local_tag,
209 current_session->modified_time,
210 current_session->windows[window_id]);
214 local_tab_pool_.DeleteUnassociatedTabNodes(change_output);
215 session_tracker_.CleanupSession(local_tag);
217 // Always update the header. Sync takes care of dropping this update
218 // if the entity specifics are identical (i.e windows, client name did
220 sync_pb::EntitySpecifics entity;
221 entity.mutable_session()->CopyFrom(specifics);
222 syncer::SyncData data = syncer::SyncData::CreateLocalData(
223 current_machine_tag(), current_session_name_, entity);
224 change_output->push_back(syncer::SyncChange(
225 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
228 void SessionsSyncManager::AssociateTab(SyncedTabDelegate* const tab,
229 syncer::SyncChangeList* change_output) {
230 DCHECK(tab->HasWebContents());
231 SessionID::id_type tab_id = tab->GetSessionId();
232 if (tab->IsBeingDestroyed()) {
233 // This tab is closing.
234 TabLinksMap::iterator tab_iter = local_tab_map_.find(tab_id);
235 if (tab_iter == local_tab_map_.end()) {
236 // We aren't tracking this tab (for example, sync setting page).
239 local_tab_pool_.FreeTabNode(tab_iter->second->tab_node_id(),
241 local_tab_map_.erase(tab_iter);
244 if (!ShouldSyncTab(*tab))
247 TabLinksMap::iterator local_tab_map_iter = local_tab_map_.find(tab_id);
248 TabLink* tab_link = NULL;
249 int tab_node_id(TabNodePool2::kInvalidTabNodeID);
251 if (local_tab_map_iter == local_tab_map_.end()) {
252 tab_node_id = tab->GetSyncId();
253 // If there is an old sync node for the tab, reuse it. If this is a new
254 // tab, get a sync node for it.
255 if (!local_tab_pool_.IsUnassociatedTabNode(tab_node_id)) {
256 tab_node_id = local_tab_pool_.GetFreeTabNode(change_output);
257 tab->SetSyncId(tab_node_id);
259 local_tab_pool_.AssociateTabNode(tab_node_id, tab_id);
260 tab_link = new TabLink(tab_node_id, tab);
261 local_tab_map_[tab_id] = make_linked_ptr<TabLink>(tab_link);
263 // This tab is already associated with a sync node, reuse it.
264 // Note: on some platforms the tab object may have changed, so we ensure
265 // the tab link is up to date.
266 tab_link = local_tab_map_iter->second.get();
267 local_tab_map_iter->second->set_tab(tab);
270 DCHECK_NE(tab_link->tab_node_id(), TabNodePool2::kInvalidTabNodeID);
271 DVLOG(1) << "Reloading tab " << tab_id << " from window "
272 << tab->GetWindowId();
274 // Write to sync model.
275 sync_pb::EntitySpecifics specifics;
276 LocalTabDelegateToSpecifics(*tab, specifics.mutable_session());
277 syncer::SyncData data = syncer::SyncData::CreateLocalData(
278 TabNodePool2::TabIdToTag(current_machine_tag_,
280 current_session_name_,
282 change_output->push_back(syncer::SyncChange(
283 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
285 const GURL new_url = GetCurrentVirtualURL(*tab);
286 if (new_url != tab_link->url()) {
287 tab_link->set_url(new_url);
288 favicon_cache_.OnFaviconVisited(new_url, GetCurrentFaviconURL(*tab));
291 session_tracker_.GetSession(current_machine_tag())->modified_time =
295 void SessionsSyncManager::OnLocalTabModified(
296 const SyncedTabDelegate& modified_tab, syncer::SyncError* error) {
297 NOTIMPLEMENTED() << "TODO(tim): SessionModelAssociator::Observe equivalent.";
300 void SessionsSyncManager::OnBrowserOpened() {
301 NOTIMPLEMENTED() << "TODO(tim): SessionModelAssociator::Observe equivalent.";
304 bool SessionsSyncManager::ShouldSyncTab(const SyncedTabDelegate& tab) const {
305 if (tab.profile() != profile_)
308 if (SyncedWindowDelegate::FindSyncedWindowDelegateWithId(
309 tab.GetWindowId()) == NULL) {
313 // Does the tab have a valid NavigationEntry?
314 if (tab.ProfileIsManaged() && tab.GetBlockedNavigations()->size() > 0)
317 int entry_count = tab.GetEntryCount();
318 if (entry_count == 0)
319 return false; // This deliberately ignores a new pending entry.
321 int pending_index = tab.GetPendingEntryIndex();
322 bool found_valid_url = false;
323 for (int i = 0; i < entry_count; ++i) {
324 const content::NavigationEntry* entry = (i == pending_index) ?
325 tab.GetPendingEntry() : tab.GetEntryAtIndex(i);
328 const GURL& virtual_url = entry->GetVirtualURL();
329 if (virtual_url.is_valid() &&
330 !virtual_url.SchemeIs(chrome::kChromeUIScheme) &&
331 !virtual_url.SchemeIs(chrome::kChromeNativeScheme) &&
332 !virtual_url.SchemeIsFile()) {
333 found_valid_url = true;
336 return found_valid_url;
340 bool SessionsSyncManager::ShouldSyncWindow(
341 const SyncedWindowDelegate* window) {
344 return window->IsTypeTabbed() || window->IsTypePopup();
347 void SessionsSyncManager::ForwardRelevantFaviconUpdatesToFaviconCache(
348 const std::set<GURL>& updated_favicon_page_urls) {
350 "TODO(tim): SessionModelAssociator::FaviconsUpdated equivalent.";
353 void SessionsSyncManager::StopSyncing(syncer::ModelType type) {
357 syncer::SyncDataList SessionsSyncManager::GetAllSyncData(
358 syncer::ModelType type) const {
360 return syncer::SyncDataList();
363 syncer::SyncError SessionsSyncManager::ProcessSyncChanges(
364 const tracked_objects::Location& from_here,
365 const syncer::SyncChangeList& change_list) {
366 if (!sync_processor_.get()) {
367 syncer::SyncError error(FROM_HERE,
368 syncer::SyncError::DATATYPE_ERROR,
369 "Models not yet associated.",
374 for (syncer::SyncChangeList::const_iterator it = change_list.begin();
375 it != change_list.end(); ++it) {
376 DCHECK(it->IsValid());
377 DCHECK(it->sync_data().GetSpecifics().has_session());
378 const sync_pb::SessionSpecifics& session =
379 it->sync_data().GetSpecifics().session();
380 switch (it->change_type()) {
381 case syncer::SyncChange::ACTION_DELETE:
382 // Deletions are all or nothing (since we only ever delete entire
383 // sessions). Therefore we don't care if it's a tab node or meta node,
384 // and just ensure we've disassociated.
385 if (current_machine_tag() == session.session_tag()) {
386 // Another client has attempted to delete our local data (possibly by
387 // error or a clock is inaccurate). Just ignore the deletion for now
388 // to avoid any possible ping-pong delete/reassociate sequence.
389 LOG(WARNING) << "Local session data deleted. Ignoring until next "
390 << "local navigation event.";
391 } else if (session.has_header()) {
392 // Disassociate only when header node is deleted. For tab node
393 // deletions, the header node will be updated and foreign tab will
395 DisassociateForeignSession(session.session_tag());
398 case syncer::SyncChange::ACTION_ADD:
399 case syncer::SyncChange::ACTION_UPDATE:
400 if (current_machine_tag() == session.session_tag()) {
401 // We should only ever receive a change to our own machine's session
402 // info if encryption was turned on. In that case, the data is still
403 // the same, so we can ignore.
404 LOG(WARNING) << "Dropping modification to local session.";
405 return syncer::SyncError();
407 UpdateTrackerWithForeignSession(
408 session, it->sync_data().GetRemoteModifiedTime());
411 NOTREACHED() << "Processing sync changes failed, unknown change type.";
415 content::NotificationService::current()->Notify(
416 chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
417 content::Source<Profile>(profile_),
418 content::NotificationService::NoDetails());
419 return syncer::SyncError();
422 syncer::SyncChange SessionsSyncManager::TombstoneTab(
423 const sync_pb::SessionSpecifics& tab) {
424 if (!tab.has_tab_node_id()) {
425 LOG(WARNING) << "Old sessions node without tab node id; can't tombstone.";
426 return syncer::SyncChange();
428 return syncer::SyncChange(
430 SyncChange::ACTION_DELETE,
431 SyncData::CreateLocalDelete(
432 TabNodePool2::TabIdToTag(current_machine_tag(),
438 bool SessionsSyncManager::GetAllForeignSessions(
439 std::vector<const SyncedSession*>* sessions) {
440 return session_tracker_.LookupAllForeignSessions(sessions);
443 bool SessionsSyncManager::InitFromSyncModel(
444 const syncer::SyncDataList& sync_data,
445 syncer::SyncChangeList* new_changes) {
446 bool found_current_header = false;
447 for (syncer::SyncDataList::const_iterator it = sync_data.begin();
448 it != sync_data.end();
450 const syncer::SyncData& data = *it;
451 DCHECK(data.GetSpecifics().has_session());
452 const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session();
453 if (specifics.session_tag().empty() ||
454 (specifics.has_tab() && (!specifics.has_tab_node_id() ||
455 !specifics.tab().has_tab_id()))) {
456 syncer::SyncChange tombstone(TombstoneTab(specifics));
457 if (tombstone.IsValid())
458 new_changes->push_back(tombstone);
459 } else if (specifics.session_tag() != current_machine_tag()) {
460 UpdateTrackerWithForeignSession(specifics, data.GetRemoteModifiedTime());
462 // This is previously stored local session information.
463 if (specifics.has_header() && !found_current_header) {
464 // This is our previous header node, reuse it.
465 found_current_header = true;
466 if (specifics.header().has_client_name())
467 current_session_name_ = specifics.header().client_name();
469 if (specifics.has_header() || !specifics.has_tab()) {
470 LOG(WARNING) << "Found more than one session header node with local "
472 syncer::SyncChange tombstone(TombstoneTab(specifics));
473 if (tombstone.IsValid())
474 new_changes->push_back(tombstone);
476 // This is a valid old tab node, add it to the pool so it can be
477 // reused for reassociation.
478 local_tab_pool_.AddTabNode(specifics.tab_node_id());
483 return found_current_header;
486 void SessionsSyncManager::UpdateTrackerWithForeignSession(
487 const sync_pb::SessionSpecifics& specifics,
488 const base::Time& modification_time) {
489 std::string foreign_session_tag = specifics.session_tag();
490 DCHECK_NE(foreign_session_tag, current_machine_tag());
492 SyncedSession* foreign_session =
493 session_tracker_.GetSession(foreign_session_tag);
494 if (specifics.has_header()) {
495 // Read in the header data for this foreign session.
496 // Header data contains window information and ordered tab id's for each
499 // Load (or create) the SyncedSession object for this client.
500 const sync_pb::SessionHeader& header = specifics.header();
501 PopulateSessionHeaderFromSpecifics(header,
505 // Reset the tab/window tracking for this session (must do this before
506 // we start calling PutWindowInSession and PutTabInWindow so that all
507 // unused tabs/windows get cleared by the CleanupSession(...) call).
508 session_tracker_.ResetSessionTracking(foreign_session_tag);
510 // Process all the windows and their tab information.
511 int num_windows = header.window_size();
512 DVLOG(1) << "Associating " << foreign_session_tag << " with "
513 << num_windows << " windows.";
515 for (int i = 0; i < num_windows; ++i) {
516 const sync_pb::SessionWindow& window_s = header.window(i);
517 SessionID::id_type window_id = window_s.window_id();
518 session_tracker_.PutWindowInSession(foreign_session_tag,
520 BuildSyncedSessionFromSpecifics(foreign_session_tag,
523 foreign_session->windows[window_id]);
525 // Delete any closed windows and unused tabs as necessary.
526 session_tracker_.CleanupSession(foreign_session_tag);
527 } else if (specifics.has_tab()) {
528 const sync_pb::SessionTab& tab_s = specifics.tab();
529 SessionID::id_type tab_id = tab_s.tab_id();
531 session_tracker_.GetTab(foreign_session_tag,
533 specifics.tab_node_id());
535 // Update SessionTab based on protobuf.
536 tab->SetFromSyncData(tab_s, modification_time);
538 // If a favicon or favicon urls are present, load the URLs and visit
539 // times into the in-memory favicon cache.
540 RefreshFaviconVisitTimesFromForeignTab(tab_s, modification_time);
542 // Update the last modified time.
543 if (foreign_session->modified_time < modification_time)
544 foreign_session->modified_time = modification_time;
546 LOG(WARNING) << "Ignoring foreign session node with missing header/tab "
547 << "fields and tag " << foreign_session_tag << ".";
551 void SessionsSyncManager::InitializeCurrentMachineTag() {
552 DCHECK(current_machine_tag_.empty());
553 std::string persisted_guid;
554 persisted_guid = sync_prefs_->GetSyncSessionsGUID();
555 if (!persisted_guid.empty()) {
556 current_machine_tag_ = persisted_guid;
557 DVLOG(1) << "Restoring persisted session sync guid: " << persisted_guid;
559 current_machine_tag_ = BuildMachineTag(delegate_->GetCacheGuid());
560 DVLOG(1) << "Creating session sync guid: " << current_machine_tag_;
561 sync_prefs_->SetSyncSessionsGUID(current_machine_tag_);
564 local_tab_pool_.SetMachineTag(current_machine_tag_);
568 void SessionsSyncManager::PopulateSessionHeaderFromSpecifics(
569 const sync_pb::SessionHeader& header_specifics,
571 SyncedSession* session_header) {
572 if (header_specifics.has_client_name())
573 session_header->session_name = header_specifics.client_name();
574 if (header_specifics.has_device_type()) {
575 switch (header_specifics.device_type()) {
576 case sync_pb::SyncEnums_DeviceType_TYPE_WIN:
577 session_header->device_type = SyncedSession::TYPE_WIN;
579 case sync_pb::SyncEnums_DeviceType_TYPE_MAC:
580 session_header->device_type = SyncedSession::TYPE_MACOSX;
582 case sync_pb::SyncEnums_DeviceType_TYPE_LINUX:
583 session_header->device_type = SyncedSession::TYPE_LINUX;
585 case sync_pb::SyncEnums_DeviceType_TYPE_CROS:
586 session_header->device_type = SyncedSession::TYPE_CHROMEOS;
588 case sync_pb::SyncEnums_DeviceType_TYPE_PHONE:
589 session_header->device_type = SyncedSession::TYPE_PHONE;
591 case sync_pb::SyncEnums_DeviceType_TYPE_TABLET:
592 session_header->device_type = SyncedSession::TYPE_TABLET;
594 case sync_pb::SyncEnums_DeviceType_TYPE_OTHER:
595 // Intentionally fall-through
597 session_header->device_type = SyncedSession::TYPE_OTHER;
601 session_header->modified_time = mtime;
605 void SessionsSyncManager::BuildSyncedSessionFromSpecifics(
606 const std::string& session_tag,
607 const sync_pb::SessionWindow& specifics,
609 SessionWindow* session_window) {
610 if (specifics.has_window_id())
611 session_window->window_id.set_id(specifics.window_id());
612 if (specifics.has_selected_tab_index())
613 session_window->selected_tab_index = specifics.selected_tab_index();
614 if (specifics.has_browser_type()) {
615 if (specifics.browser_type() ==
616 sync_pb::SessionWindow_BrowserType_TYPE_TABBED) {
617 session_window->type = 1;
619 session_window->type = 2;
622 session_window->timestamp = mtime;
623 session_window->tabs.resize(specifics.tab_size(), NULL);
624 for (int i = 0; i < specifics.tab_size(); i++) {
625 SessionID::id_type tab_id = specifics.tab(i);
626 session_tracker_.PutTabInWindow(session_tag,
627 session_window->window_id.id(),
633 void SessionsSyncManager::RefreshFaviconVisitTimesFromForeignTab(
634 const sync_pb::SessionTab& tab, const base::Time& modification_time) {
635 // First go through and iterate over all the navigations, checking if any
636 // have valid favicon urls.
637 for (int i = 0; i < tab.navigation_size(); ++i) {
638 if (!tab.navigation(i).favicon_url().empty()) {
639 const std::string& page_url = tab.navigation(i).virtual_url();
640 const std::string& favicon_url = tab.navigation(i).favicon_url();
641 favicon_cache_.OnReceivedSyncFavicon(GURL(page_url),
644 syncer::TimeToProtoTime(
650 bool SessionsSyncManager::GetSyncedFaviconForPageURL(
651 const std::string& page_url,
652 scoped_refptr<base::RefCountedMemory>* favicon_png) const {
653 return favicon_cache_.GetSyncedFaviconForPageURL(GURL(page_url), favicon_png);
656 void SessionsSyncManager::DeleteForeignSession(
657 const std::string& tag, syncer::SyncChangeList* change_output) {
658 if (tag == current_machine_tag()) {
659 LOG(ERROR) << "Attempting to delete local session. This is not currently "
664 std::set<int> tab_node_ids_to_delete;
665 session_tracker_.LookupTabNodeIds(tag, &tab_node_ids_to_delete);
666 if (!DisassociateForeignSession(tag)) {
667 // We don't have any data for this session, our work here is done!
671 // Prepare deletes for the meta-node as well as individual tab nodes.
672 change_output->push_back(syncer::SyncChange(
674 SyncChange::ACTION_DELETE,
675 SyncData::CreateLocalDelete(tag, syncer::SESSIONS)));
677 for (std::set<int>::const_iterator it = tab_node_ids_to_delete.begin();
678 it != tab_node_ids_to_delete.end();
680 change_output->push_back(syncer::SyncChange(
682 SyncChange::ACTION_DELETE,
683 SyncData::CreateLocalDelete(TabNodePool2::TabIdToTag(tag, *it),
688 bool SessionsSyncManager::DisassociateForeignSession(
689 const std::string& foreign_session_tag) {
690 if (foreign_session_tag == current_machine_tag()) {
691 DVLOG(1) << "Local session deleted! Doing nothing until a navigation is "
695 DVLOG(1) << "Disassociating session " << foreign_session_tag;
696 return session_tracker_.DeleteSession(foreign_session_tag);
700 GURL SessionsSyncManager::GetCurrentVirtualURL(
701 const SyncedTabDelegate& tab_delegate) {
702 const int current_index = tab_delegate.GetCurrentEntryIndex();
703 const int pending_index = tab_delegate.GetPendingEntryIndex();
704 const NavigationEntry* current_entry =
705 (current_index == pending_index) ?
706 tab_delegate.GetPendingEntry() :
707 tab_delegate.GetEntryAtIndex(current_index);
708 return current_entry->GetVirtualURL();
712 GURL SessionsSyncManager::GetCurrentFaviconURL(
713 const SyncedTabDelegate& tab_delegate) {
714 const int current_index = tab_delegate.GetCurrentEntryIndex();
715 const int pending_index = tab_delegate.GetPendingEntryIndex();
716 const NavigationEntry* current_entry =
717 (current_index == pending_index) ?
718 tab_delegate.GetPendingEntry() :
719 tab_delegate.GetEntryAtIndex(current_index);
720 return (current_entry->GetFavicon().valid ?
721 current_entry->GetFavicon().url :
725 void SessionsSyncManager::LocalTabDelegateToSpecifics(
726 const SyncedTabDelegate& tab_delegate,
727 sync_pb::SessionSpecifics* specifics) {
728 SessionTab* session_tab = NULL;
730 session_tracker_.GetTab(current_machine_tag(),
731 tab_delegate.GetSessionId(),
732 tab_delegate.GetSyncId());
733 SetSessionTabFromDelegate(tab_delegate, base::Time::Now(), session_tab);
734 sync_pb::SessionTab tab_s = session_tab->ToSyncData();
735 specifics->set_session_tag(current_machine_tag_);
736 specifics->set_tab_node_id(tab_delegate.GetSyncId());
737 specifics->mutable_tab()->CopyFrom(tab_s);
740 void SessionsSyncManager::UpdateTabIdIfNecessary(
741 const SyncedTabDelegate& tab_delegate,
742 SessionID::id_type new_tab_id,
743 syncer::SyncChangeList* change_output) {
744 DCHECK_NE(tab_delegate.GetSyncId(), TabNodePool2::kInvalidTabNodeID);
745 SessionID::id_type old_tab_id =
746 local_tab_pool_.GetTabIdFromTabNodeId(tab_delegate.GetSyncId());
747 if (old_tab_id != new_tab_id) {
748 // Rewrite the tab. We don't have a way to get the old
749 // specifics here currently.
750 // TODO(tim): Is this too slow? Should we cache specifics?
751 sync_pb::EntitySpecifics specifics;
752 LocalTabDelegateToSpecifics(tab_delegate,
753 specifics.mutable_session());
755 // Update tab node pool with the new association.
756 local_tab_pool_.ReassociateTabNode(tab_delegate.GetSyncId(), new_tab_id);
757 syncer::SyncData data = syncer::SyncData::CreateLocalData(
758 TabNodePool2::TabIdToTag(current_machine_tag_,
759 tab_delegate.GetSyncId()),
760 current_session_name_, specifics);
761 change_output->push_back(syncer::SyncChange(
762 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data));
767 void SessionsSyncManager::SetSessionTabFromDelegate(
768 const SyncedTabDelegate& tab_delegate,
770 SessionTab* session_tab) {
772 session_tab->window_id.set_id(tab_delegate.GetWindowId());
773 session_tab->tab_id.set_id(tab_delegate.GetSessionId());
774 session_tab->tab_visual_index = 0;
775 session_tab->current_navigation_index = tab_delegate.GetCurrentEntryIndex();
776 session_tab->pinned = tab_delegate.IsPinned();
777 session_tab->extension_app_id = tab_delegate.GetExtensionAppId();
778 session_tab->user_agent_override.clear();
779 session_tab->timestamp = mtime;
780 const int current_index = tab_delegate.GetCurrentEntryIndex();
781 const int pending_index = tab_delegate.GetPendingEntryIndex();
782 const int min_index = std::max(0, current_index - kMaxSyncNavigationCount);
783 const int max_index = std::min(current_index + kMaxSyncNavigationCount,
784 tab_delegate.GetEntryCount());
785 bool is_managed = tab_delegate.ProfileIsManaged();
786 session_tab->navigations.clear();
788 #if !defined(OS_ANDROID)
789 // For getting navigation time in network time.
790 NavigationTimeHelper* nav_time_helper =
791 tab_delegate.HasWebContents() ?
792 NavigationTimeHelper::FromWebContents(tab_delegate.GetWebContents()) :
796 for (int i = min_index; i < max_index; ++i) {
797 const NavigationEntry* entry = (i == pending_index) ?
798 tab_delegate.GetPendingEntry() : tab_delegate.GetEntryAtIndex(i);
800 if (!entry->GetVirtualURL().is_valid())
803 scoped_ptr<content::NavigationEntry> network_time_entry(
804 content::NavigationEntry::Create(*entry));
805 #if !defined(OS_ANDROID)
806 if (nav_time_helper) {
807 network_time_entry->SetTimestamp(
808 nav_time_helper->GetNavigationTime(entry));
812 session_tab->navigations.push_back(
813 SerializedNavigationEntry::FromNavigationEntry(i, *network_time_entry));
815 session_tab->navigations.back().set_blocked_state(
816 SerializedNavigationEntry::STATE_ALLOWED);
821 const std::vector<const NavigationEntry*>& blocked_navigations =
822 *tab_delegate.GetBlockedNavigations();
823 int offset = session_tab->navigations.size();
824 for (size_t i = 0; i < blocked_navigations.size(); ++i) {
825 session_tab->navigations.push_back(
826 SerializedNavigationEntry::FromNavigationEntry(
827 i + offset, *blocked_navigations[i]));
828 session_tab->navigations.back().set_blocked_state(
829 SerializedNavigationEntry::STATE_BLOCKED);
830 // TODO(bauerb): Add categories
833 session_tab->session_storage_persistent_id.clear();
836 }; // namespace browser_sync