| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync_sessions/sessions_sync_manager.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/bind_helpers.h" |
| #include "base/format_macros.h" |
| #include "base/logging.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "build/build_config.h" |
| #include "components/sync/base/hash_util.h" |
| #include "components/sync/device_info/local_device_info_provider.h" |
| #include "components/sync/model/sync_error.h" |
| #include "components/sync/model/sync_error_factory.h" |
| #include "components/sync/model/sync_merge_result.h" |
| #include "components/sync/model/time.h" |
| #include "components/sync_sessions/local_session_event_router.h" |
| #include "components/sync_sessions/sync_sessions_client.h" |
| #include "components/sync_sessions/tab_node_pool.h" |
| |
| using syncer::DeviceInfo; |
| using syncer::LocalDeviceInfoProvider; |
| using syncer::SyncChange; |
| using syncer::SyncData; |
| |
| namespace sync_sessions { |
| |
| namespace { |
| |
| // Maximum number of favicons to sync. |
| // TODO(zea): pull this from the server. |
| const int kMaxSyncFavicons = 200; |
| |
| // Default number of days without activity after which a session is considered |
| // stale and becomes a candidate for garbage collection. |
| const int kDefaultStaleSessionThresholdDays = 14; // 2 weeks. |
| |
| std::string TabNodeIdToTag(const std::string& machine_tag, int tab_node_id) { |
| CHECK_GT(tab_node_id, TabNodePool::kInvalidTabNodeID) |
| << "https://crbug.com/639009"; |
| return base::StringPrintf("%s %d", machine_tag.c_str(), tab_node_id); |
| } |
| |
| std::string TagFromSpecifics(const sync_pb::SessionSpecifics& specifics) { |
| if (specifics.has_header()) { |
| return specifics.session_tag(); |
| } else if (specifics.has_tab()) { |
| return TabNodeIdToTag(specifics.session_tag(), specifics.tab_node_id()); |
| } else { |
| return std::string(); |
| } |
| } |
| |
| sync_pb::SessionSpecifics SessionTabToSpecifics( |
| const sessions::SessionTab& session_tab, |
| const std::string& local_tag, |
| int tab_node_id) { |
| sync_pb::SessionSpecifics specifics; |
| session_tab.ToSyncData().Swap(specifics.mutable_tab()); |
| specifics.set_session_tag(local_tag); |
| specifics.set_tab_node_id(tab_node_id); |
| return specifics; |
| } |
| |
| void AppendDeletionsForTabNodes(const std::set<int>& tab_node_ids, |
| const std::string& machine_tag, |
| syncer::SyncChangeList* change_output) { |
| for (std::set<int>::const_iterator it = tab_node_ids.begin(); |
| it != tab_node_ids.end(); ++it) { |
| change_output->push_back(syncer::SyncChange( |
| FROM_HERE, SyncChange::ACTION_DELETE, |
| SyncData::CreateLocalDelete(TabNodeIdToTag(machine_tag, *it), |
| syncer::SESSIONS))); |
| } |
| } |
| |
| class SyncChangeListWriteBatch |
| : public LocalSessionEventHandlerImpl::WriteBatch { |
| public: |
| SyncChangeListWriteBatch( |
| const std::string& machine_tag, |
| const std::string& session_name, |
| base::OnceCallback<void(const syncer::SyncChangeList&)> commit_cb) |
| : machine_tag_(machine_tag), |
| session_name_(session_name), |
| commit_cb_(std::move(commit_cb)) {} |
| |
| syncer::SyncChangeList* sync_change_list() { return &changes_; } |
| |
| // WriteBatch implementation. |
| void Delete(int tab_node_id) override { |
| changes_.push_back(syncer::SyncChange( |
| FROM_HERE, SyncChange::ACTION_DELETE, |
| SyncData::CreateLocalDelete(TabNodeIdToTag(machine_tag_, tab_node_id), |
| syncer::SESSIONS))); |
| } |
| |
| void Add(std::unique_ptr<sync_pb::SessionSpecifics> specifics) override { |
| sync_pb::EntitySpecifics entity_specifics; |
| specifics->Swap(entity_specifics.mutable_session()); |
| changes_.push_back( |
| syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, |
| syncer::SyncData::CreateLocalData( |
| TagFromSpecifics(entity_specifics.session()), |
| session_name_, entity_specifics))); |
| } |
| |
| void Update(std::unique_ptr<sync_pb::SessionSpecifics> specifics) override { |
| sync_pb::EntitySpecifics entity_specifics; |
| specifics->Swap(entity_specifics.mutable_session()); |
| changes_.push_back( |
| syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, |
| syncer::SyncData::CreateLocalData( |
| TagFromSpecifics(entity_specifics.session()), |
| session_name_, entity_specifics))); |
| } |
| |
| void Commit() override { std::move(commit_cb_).Run(changes_); } |
| |
| private: |
| const std::string machine_tag_; |
| const std::string session_name_; |
| base::OnceCallback<void(const syncer::SyncChangeList&)> commit_cb_; |
| syncer::SyncChangeList changes_; |
| }; |
| |
| } // namespace |
| |
| // |local_device| is owned by ProfileSyncService, its lifetime exceeds |
| // lifetime of SessionSyncManager. |
| SessionsSyncManager::SessionsSyncManager( |
| sync_sessions::SyncSessionsClient* sessions_client, |
| syncer::SessionSyncPrefs* sync_prefs, |
| LocalDeviceInfoProvider* local_device, |
| const base::RepeatingClosure& sessions_updated_callback) |
| : sessions_client_(sessions_client), |
| session_tracker_(sessions_client), |
| favicon_cache_(sessions_client->GetFaviconService(), |
| sessions_client->GetHistoryService(), |
| kMaxSyncFavicons), |
| open_tabs_ui_delegate_( |
| sessions_client, |
| &session_tracker_, |
| &favicon_cache_, |
| base::BindRepeating(&SessionsSyncManager::DeleteForeignSessionFromUI, |
| base::Unretained(this))), |
| local_tab_pool_out_of_sync_(true), |
| sync_prefs_(sync_prefs), |
| local_device_(local_device), |
| stale_session_threshold_days_(kDefaultStaleSessionThresholdDays), |
| sessions_updated_callback_(sessions_updated_callback) {} |
| |
| SessionsSyncManager::~SessionsSyncManager() {} |
| |
| // Returns the GUID-based string that should be used for |
| // |SessionsSyncManager::current_machine_tag_|. |
| static std::string BuildMachineTag(const std::string& cache_guid) { |
| std::string machine_tag = "session_sync"; |
| machine_tag.append(cache_guid); |
| return machine_tag; |
| } |
| |
| void SessionsSyncManager::ScheduleGarbageCollection() { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&SessionsSyncManager::DoGarbageCollection, |
| base::AsWeakPtr(this))); |
| } |
| |
| void SessionsSyncManager::OnSessionRestoreComplete() { |
| // Do nothing. The very same event is plumbed manually to |
| // SessionDataTypeController by ProfileSyncComponentsFactoryImpl. |
| } |
| |
| syncer::SyncableService* SessionsSyncManager::GetSyncableService() { |
| return this; |
| } |
| |
| syncer::ModelTypeSyncBridge* SessionsSyncManager::GetModelTypeSyncBridge() { |
| return nullptr; |
| } |
| |
| syncer::SyncMergeResult SessionsSyncManager::MergeDataAndStartSyncing( |
| syncer::ModelType type, |
| const syncer::SyncDataList& initial_sync_data, |
| std::unique_ptr<syncer::SyncChangeProcessor> sync_processor, |
| std::unique_ptr<syncer::SyncErrorFactory> error_handler) { |
| syncer::SyncMergeResult merge_result(type); |
| DCHECK(session_tracker_.Empty()); |
| DCHECK(!local_session_event_handler_); |
| |
| error_handler_ = std::move(error_handler); |
| sync_processor_ = std::move(sync_processor); |
| |
| // SessionDataTypeController ensures that the local device info |
| // is available before activating this datatype. |
| DCHECK(local_device_); |
| const DeviceInfo* local_device_info = local_device_->GetLocalDeviceInfo(); |
| if (!local_device_info) { |
| merge_result.set_error(error_handler_->CreateAndUploadError( |
| FROM_HERE, "Failed to get local device info.")); |
| return merge_result; |
| } |
| |
| current_session_name_ = local_device_info->client_name(); |
| |
| // It's possible(via RebuildAssociations) for lost_navigations_recorder_ to |
| // persist between sync being stopped and started. If it did persist, it's |
| // already associated with |sync_processor|, so leave it alone. |
| if (!lost_navigations_recorder_.get()) { |
| lost_navigations_recorder_ = |
| std::make_unique<sync_sessions::LostNavigationsRecorder>(); |
| sync_processor_->AddLocalChangeObserver(lost_navigations_recorder_.get()); |
| } |
| |
| // Make sure we have a machine tag. We do this now (versus earlier) as it's |
| // a conveniently safe time to assert sync is ready and the cache_guid is |
| // initialized. |
| if (current_machine_tag_.empty()) { |
| InitializeCurrentMachineTag(local_device_->GetLocalSyncCacheGUID()); |
| } |
| |
| session_tracker_.InitLocalSession(current_machine_tag_, current_session_name_, |
| local_device_info->device_type()); |
| |
| // TODO(crbug.com/681921): Revisit the somewhat ugly use below of |
| // SyncChangeListWriteBatch. Ideally InitFromSyncModel() could use the |
| // WriteBatch API as well. |
| SyncChangeListWriteBatch batch(current_machine_tag(), current_session_name_, |
| /*commit_cb=*/base::DoNothing()); |
| |
| // First, we iterate over sync data to update our session_tracker_. |
| if (!InitFromSyncModel(initial_sync_data, batch.sync_change_list())) { |
| // The sync db didn't have a header node for us. Create one. |
| auto specifics = std::make_unique<sync_pb::SessionSpecifics>(); |
| specifics->set_session_tag(current_machine_tag()); |
| sync_pb::SessionHeader* header_s = specifics->mutable_header(); |
| header_s->set_client_name(current_session_name_); |
| header_s->set_device_type(local_device_info->device_type()); |
| batch.Add(std::move(specifics)); |
| } |
| |
| #if defined(OS_ANDROID) |
| std::string sync_machine_tag( |
| BuildMachineTag(local_device_->GetLocalSyncCacheGUID())); |
| if (current_machine_tag().compare(sync_machine_tag) != 0) |
| DeleteForeignSessionInternal(sync_machine_tag, batch.sync_change_list()); |
| #endif |
| |
| // Check if anything has changed on the local client side. |
| local_session_event_handler_ = std::make_unique<LocalSessionEventHandlerImpl>( |
| /*delegate=*/this, sessions_client_, &session_tracker_, &batch); |
| local_tab_pool_out_of_sync_ = false; |
| |
| merge_result.set_error(sync_processor_->ProcessSyncChanges( |
| FROM_HERE, *batch.sync_change_list())); |
| |
| sessions_client_->GetLocalSessionEventRouter()->StartRoutingTo( |
| local_session_event_handler_.get()); |
| return merge_result; |
| } |
| |
| bool SessionsSyncManager::RebuildAssociations() { |
| syncer::SyncDataList data(sync_processor_->GetAllSyncData(syncer::SESSIONS)); |
| std::unique_ptr<syncer::SyncErrorFactory> error_handler( |
| std::move(error_handler_)); |
| std::unique_ptr<syncer::SyncChangeProcessor> processor( |
| std::move(sync_processor_)); |
| |
| StopSyncing(syncer::SESSIONS); |
| syncer::SyncMergeResult merge_result = MergeDataAndStartSyncing( |
| syncer::SESSIONS, data, std::move(processor), std::move(error_handler)); |
| return !merge_result.error().IsSet(); |
| } |
| |
| void SessionsSyncManager::StopSyncing(syncer::ModelType type) { |
| sessions_client_->GetLocalSessionEventRouter()->Stop(); |
| local_session_event_handler_.reset(); |
| if (sync_processor_.get() && lost_navigations_recorder_.get()) { |
| sync_processor_->RemoveLocalChangeObserver( |
| lost_navigations_recorder_.get()); |
| lost_navigations_recorder_.reset(); |
| } |
| sync_processor_.reset(nullptr); |
| error_handler_.reset(); |
| session_tracker_.Clear(); |
| current_machine_tag_.clear(); |
| current_session_name_.clear(); |
| } |
| |
| syncer::SyncDataList SessionsSyncManager::GetAllSyncData( |
| syncer::ModelType type) const { |
| syncer::SyncDataList list; |
| const SyncedSession* session = session_tracker_.LookupLocalSession(); |
| if (!session) |
| return syncer::SyncDataList(); |
| |
| // First construct the header node. |
| sync_pb::EntitySpecifics header_entity; |
| header_entity.mutable_session()->set_session_tag(current_machine_tag()); |
| sync_pb::SessionHeader* header_specifics = |
| header_entity.mutable_session()->mutable_header(); |
| header_specifics->MergeFrom(session->ToSessionHeaderProto()); |
| syncer::SyncData data = syncer::SyncData::CreateLocalData( |
| current_machine_tag(), current_session_name_, header_entity); |
| list.push_back(data); |
| |
| for (const auto& win_iter : session->windows) { |
| for (const auto& tab : win_iter.second->wrapped_window.tabs) { |
| // TODO(zea): replace with with the correct tab node id once there's a |
| // sync specific wrapper for SessionTab. This method is only used in |
| // tests though, so it's fine for now. https://crbug.com/662597 |
| int tab_node_id = 0; |
| sync_pb::EntitySpecifics entity; |
| SessionTabToSpecifics(*tab, current_machine_tag(), tab_node_id) |
| .Swap(entity.mutable_session()); |
| syncer::SyncData data = syncer::SyncData::CreateLocalData( |
| TabNodeIdToTag(current_machine_tag(), tab_node_id), |
| current_session_name_, entity); |
| list.push_back(data); |
| } |
| } |
| return list; |
| } |
| |
| syncer::SyncError SessionsSyncManager::ProcessSyncChanges( |
| const base::Location& from_here, |
| const syncer::SyncChangeList& change_list) { |
| if (!sync_processor_.get()) { |
| syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, |
| "Models not yet associated.", syncer::SESSIONS); |
| return error; |
| } |
| |
| for (syncer::SyncChangeList::const_iterator it = change_list.begin(); |
| it != change_list.end(); ++it) { |
| DCHECK(it->IsValid()); |
| DCHECK(it->sync_data().GetSpecifics().has_session()); |
| const sync_pb::SessionSpecifics& session = |
| it->sync_data().GetSpecifics().session(); |
| const base::Time mtime = |
| syncer::SyncDataRemote(it->sync_data()).GetModifiedTime(); |
| switch (it->change_type()) { |
| case syncer::SyncChange::ACTION_DELETE: |
| // Deletions are all or nothing (since we only ever delete entire |
| // sessions). Therefore we don't care if it's a tab node or meta node, |
| // and just ensure we've disassociated. |
| if (current_machine_tag() == session.session_tag()) { |
| // Another client has attempted to delete our local data (possibly by |
| // error or a clock is inaccurate). Just ignore the deletion for now |
| // to avoid any possible ping-pong delete/reassociate sequence, but |
| // remember that this happened as our TabNodePool is inconsistent. |
| local_tab_pool_out_of_sync_ = true; |
| LOG(WARNING) << "Local session data deleted. Ignoring until next " |
| << "local navigation event."; |
| } else if (session.has_header()) { |
| // Disassociate only when header node is deleted. For tab node |
| // deletions, the header node will be updated and foreign tab will |
| // get deleted. |
| DisassociateForeignSession(session.session_tag()); |
| } else if (session.has_tab()) { |
| // The challenge here is that we don't know if this tab deletion is |
| // being processed before or after the parent was updated to no longer |
| // references the tab. Or, even more extreme, the parent has been |
| // deleted as well. Tell the tracker to do what it can. The header's |
| // update will mostly get us into the correct state, the only thing |
| // this deletion needs to accomplish is make sure we never tell sync |
| // to delete this tab later during garbage collection. |
| session_tracker_.DeleteForeignTab(session.session_tag(), |
| session.tab_node_id()); |
| } |
| break; |
| case syncer::SyncChange::ACTION_ADD: |
| case syncer::SyncChange::ACTION_UPDATE: |
| if (current_machine_tag() == session.session_tag()) { |
| // We should only ever receive a change to our own machine's session |
| // info if encryption was turned on. In that case, the data is still |
| // the same, so we can ignore. |
| LOG(WARNING) << "Dropping modification to local session."; |
| return syncer::SyncError(); |
| } |
| UpdateTrackerWithSpecifics(session, mtime, &session_tracker_); |
| // If a favicon or favicon urls are present, load the URLs and visit |
| // times into the in-memory favicon cache. |
| if (session.has_tab()) { |
| favicon_cache_.UpdateMappingsFromForeignTab(session.tab(), mtime); |
| } |
| break; |
| default: |
| NOTREACHED() << "Processing sync changes failed, unknown change type."; |
| } |
| } |
| |
| if (!sessions_updated_callback_.is_null()) |
| sessions_updated_callback_.Run(); |
| return syncer::SyncError(); |
| } |
| |
| syncer::SyncChange SessionsSyncManager::TombstoneTab( |
| const sync_pb::SessionSpecifics& tab) { |
| if (!tab.has_tab_node_id()) { |
| LOG(WARNING) << "Old sessions node without tab node id; can't tombstone."; |
| return syncer::SyncChange(); |
| } else { |
| return syncer::SyncChange( |
| FROM_HERE, SyncChange::ACTION_DELETE, |
| SyncData::CreateLocalDelete( |
| TabNodeIdToTag(current_machine_tag(), tab.tab_node_id()), |
| syncer::SESSIONS)); |
| } |
| } |
| |
| bool SessionsSyncManager::InitFromSyncModel( |
| const syncer::SyncDataList& sync_data, |
| syncer::SyncChangeList* new_changes) { |
| // Map of all rewritten local ids. Because ids are reset on each restart, |
| // and id generation happens outside of Sync, all ids from a previous local |
| // session must be rewritten in order to be valid. |
| // Key: previous session id. Value: new session id. |
| std::map<int32_t, SessionID> session_id_map; |
| |
| bool found_current_header = false; |
| int bad_foreign_hash_count = 0; |
| for (syncer::SyncDataList::const_iterator it = sync_data.begin(); |
| it != sync_data.end(); ++it) { |
| const syncer::SyncData& data = *it; |
| DCHECK(data.GetSpecifics().has_session()); |
| syncer::SyncDataRemote remote(data); |
| const sync_pb::SessionSpecifics& specifics = data.GetSpecifics().session(); |
| if (specifics.session_tag().empty() || |
| (specifics.has_tab() && |
| (!specifics.has_tab_node_id() || !specifics.tab().has_tab_id()))) { |
| syncer::SyncChange tombstone(TombstoneTab(specifics)); |
| if (tombstone.IsValid()) |
| new_changes->push_back(tombstone); |
| } else if (specifics.session_tag() != current_machine_tag()) { |
| if (TagHashFromSpecifics(specifics) == remote.GetClientTagHash()) { |
| UpdateTrackerWithSpecifics(specifics, remote.GetModifiedTime(), |
| &session_tracker_); |
| // If a favicon or favicon urls are present, load the URLs and visit |
| // times into the in-memory favicon cache. |
| if (specifics.has_tab()) { |
| favicon_cache_.UpdateMappingsFromForeignTab(specifics.tab(), |
| remote.GetModifiedTime()); |
| } |
| } else { |
| // In the past, like years ago, we believe that some session data was |
| // created with bad tag hashes. This causes any change this client makes |
| // to that foreign data (like deletion through garbage collection) to |
| // trigger a data type error because the tag looking mechanism fails. So |
| // look for these and delete via remote SyncData, which uses a server id |
| // lookup mechanism instead, see https://crbug.com/604657. |
| bad_foreign_hash_count++; |
| new_changes->push_back( |
| syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, remote)); |
| } |
| } else { |
| // This is previously stored local session information. |
| if (specifics.has_header() && !found_current_header) { |
| // This is our previous header node, reuse it. |
| found_current_header = true; |
| |
| // The specifics from the SyncData are immutable. Create a mutable copy |
| // to hold the rewritten ids. |
| sync_pb::SessionSpecifics rewritten_specifics(specifics); |
| |
| // Go through and generate new tab and window ids as necessary, updating |
| // the specifics in place. |
| for (auto& window : |
| *rewritten_specifics.mutable_header()->mutable_window()) { |
| session_id_map.emplace(window.window_id(), SessionID::NewUnique()); |
| window.set_window_id(session_id_map.at(window.window_id()).id()); |
| |
| google::protobuf::RepeatedField<int>* tab_ids = window.mutable_tab(); |
| for (int i = 0; i < tab_ids->size(); i++) { |
| auto tab_iter = session_id_map.find(tab_ids->Get(i)); |
| if (tab_iter == session_id_map.end()) { |
| // SessionID::SessionID() automatically increments a static |
| // variable, forcing a new id to be generated each time. |
| session_id_map.emplace(tab_ids->Get(i), SessionID::NewUnique()); |
| } |
| *(tab_ids->Mutable(i)) = session_id_map.at(tab_ids->Get(i)).id(); |
| // Note: the tab id of the SessionTab will be updated when the tab |
| // node itself is processed. |
| } |
| } |
| |
| UpdateTrackerWithSpecifics(rewritten_specifics, |
| remote.GetModifiedTime(), &session_tracker_); |
| |
| DVLOG(1) << "Loaded local header and rewrote " << session_id_map.size() |
| << " ids."; |
| |
| } else { |
| if (specifics.has_header() || !specifics.has_tab()) { |
| LOG(WARNING) << "Found more than one session header node with local " |
| << "tag."; |
| syncer::SyncChange tombstone(TombstoneTab(specifics)); |
| if (tombstone.IsValid()) |
| new_changes->push_back(tombstone); |
| } else if (specifics.tab().tab_id() <= 0) { |
| LOG(WARNING) << "Found tab node with invalid tab id."; |
| syncer::SyncChange tombstone(TombstoneTab(specifics)); |
| if (tombstone.IsValid()) |
| new_changes->push_back(tombstone); |
| } else { |
| // This is a valid old tab node, add it to the tracker and associate |
| // it (using the new tab id). |
| DVLOG(1) << "Associating local tab " << specifics.tab().tab_id() |
| << " with node " << specifics.tab_node_id(); |
| |
| // Now file the tab under the new tab id. |
| SessionID new_tab_id = SessionID::InvalidValue(); |
| auto iter = session_id_map.find(specifics.tab().tab_id()); |
| if (iter != session_id_map.end()) { |
| new_tab_id = iter->second; |
| } else { |
| new_tab_id = SessionID::NewUnique(); |
| session_id_map.emplace(specifics.tab().tab_id(), new_tab_id); |
| } |
| DVLOG(1) << "Remapping tab " << specifics.tab().tab_id() << " to " |
| << new_tab_id; |
| |
| // The specifics from the SyncData are immutable. Create a mutable |
| // copy to hold the rewritten ids. |
| sync_pb::SessionSpecifics rewritten_specifics(specifics); |
| rewritten_specifics.mutable_tab()->set_tab_id(new_tab_id.id()); |
| session_tracker_.ReassociateLocalTab( |
| rewritten_specifics.tab_node_id(), new_tab_id); |
| UpdateTrackerWithSpecifics( |
| rewritten_specifics, remote.GetModifiedTime(), &session_tracker_); |
| } |
| } |
| } |
| } |
| |
| // Cleanup all foreign sessions, since orphaned tabs may have been added after |
| // the header. |
| for (const auto* session : |
| session_tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW)) { |
| session_tracker_.CleanupSession(session->session_tag); |
| } |
| |
| UMA_HISTOGRAM_COUNTS_100("Sync.SessionsBadForeignHashOnMergeCount", |
| bad_foreign_hash_count); |
| |
| return found_current_header; |
| } |
| |
| void SessionsSyncManager::InitializeCurrentMachineTag( |
| const std::string& cache_guid) { |
| DCHECK(current_machine_tag_.empty()); |
| std::string persisted_guid; |
| persisted_guid = sync_prefs_->GetSyncSessionsGUID(); |
| if (!persisted_guid.empty()) { |
| current_machine_tag_ = persisted_guid; |
| DVLOG(1) << "Restoring persisted session sync guid: " << persisted_guid; |
| } else { |
| DCHECK(!cache_guid.empty()); |
| current_machine_tag_ = BuildMachineTag(cache_guid); |
| DVLOG(1) << "Creating session sync guid: " << current_machine_tag_; |
| sync_prefs_->SetSyncSessionsGUID(current_machine_tag_); |
| } |
| } |
| |
| void SessionsSyncManager::DeleteForeignSessionInternal( |
| const std::string& tag, |
| syncer::SyncChangeList* change_output) { |
| if (tag == current_machine_tag()) { |
| LOG(ERROR) << "Attempting to delete local session. This is not currently " |
| << "supported."; |
| return; |
| } |
| |
| const std::set<int> tab_node_ids_to_delete = |
| session_tracker_.LookupTabNodeIds(tag); |
| if (DisassociateForeignSession(tag)) { |
| // Only tell sync to delete the header if there was one. |
| change_output->push_back( |
| syncer::SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, |
| SyncData::CreateLocalDelete(tag, syncer::SESSIONS))); |
| } |
| AppendDeletionsForTabNodes(tab_node_ids_to_delete, tag, change_output); |
| if (!sessions_updated_callback_.is_null()) |
| sessions_updated_callback_.Run(); |
| } |
| |
| void SessionsSyncManager::DeleteForeignSessionFromUI(const std::string& tag) { |
| syncer::SyncChangeList changes; |
| DeleteForeignSessionInternal(tag, &changes); |
| sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
| } |
| |
| bool SessionsSyncManager::DisassociateForeignSession( |
| const std::string& foreign_session_tag) { |
| DCHECK_NE(foreign_session_tag, current_machine_tag()); |
| DVLOG(1) << "Disassociating session " << foreign_session_tag; |
| return session_tracker_.DeleteForeignSession(foreign_session_tag); |
| } |
| |
| std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch> |
| SessionsSyncManager::CreateLocalSessionWriteBatch() { |
| return std::make_unique<SyncChangeListWriteBatch>( |
| current_machine_tag(), current_session_name_, |
| /*commit_cb=*/ |
| base::BindOnce(&SessionsSyncManager::ProcessLocalSessionSyncChanges, |
| base::AsWeakPtr(this))); |
| } |
| |
| void SessionsSyncManager::TrackLocalNavigationId(base::Time timestamp, |
| int unique_id) { |
| global_id_mapper_.TrackNavigationId(timestamp, unique_id); |
| } |
| |
| void SessionsSyncManager::OnPageFaviconUpdated(const GURL& page_url) { |
| favicon_cache_.OnPageFaviconUpdated(page_url, base::Time::Now()); |
| } |
| |
| void SessionsSyncManager::OnFaviconVisited(const GURL& page_url, |
| const GURL& favicon_url) { |
| favicon_cache_.OnFaviconVisited(page_url, favicon_url); |
| } |
| |
| FaviconCache* SessionsSyncManager::GetFaviconCache() { |
| return &favicon_cache_; |
| } |
| |
| SessionsGlobalIdMapper* SessionsSyncManager::GetGlobalIdMapper() { |
| return &global_id_mapper_; |
| } |
| |
| OpenTabsUIDelegate* SessionsSyncManager::GetOpenTabsUIDelegate() { |
| return &open_tabs_ui_delegate_; |
| } |
| |
| void SessionsSyncManager::DoGarbageCollection() { |
| // Iterate through all the sessions and delete any with age older than |
| // |stale_session_threshold_days_|. |
| syncer::SyncChangeList changes; |
| for (const auto* session : |
| session_tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW)) { |
| int session_age_in_days = |
| (base::Time::Now() - session->modified_time).InDays(); |
| if (session_age_in_days > stale_session_threshold_days_) { |
| std::string session_tag = session->session_tag; |
| DVLOG(1) << "Found stale session " << session_tag << " with age " |
| << session_age_in_days << ", deleting."; |
| DeleteForeignSessionInternal(session_tag, &changes); |
| } |
| } |
| |
| if (!changes.empty()) |
| sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
| } |
| |
| // static |
| std::string SessionsSyncManager::TagHashFromSpecifics( |
| const sync_pb::SessionSpecifics& specifics) { |
| return syncer::GenerateSyncableHash(syncer::SESSIONS, |
| TagFromSpecifics(specifics)); |
| } |
| |
| void SessionsSyncManager::ProcessLocalSessionSyncChanges( |
| const syncer::SyncChangeList& change_list) { |
| if (local_tab_pool_out_of_sync_) { |
| // If our tab pool is corrupt, pay the price of a full re-association to |
| // fix things up. This takes care of the new tab modification as well. |
| bool rebuild_association_succeeded = RebuildAssociations(); |
| DCHECK(!rebuild_association_succeeded || !local_tab_pool_out_of_sync_); |
| return; |
| } |
| |
| sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); |
| } |
| |
| } // namespace sync_sessions |