// Copyright (c) 2012 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. #ifndef CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_ #define CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_ #include #include #include #include #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/memory/linked_ptr.h" #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/time/time.h" #include "chrome/browser/sessions/session_id.h" #include "chrome/browser/sessions/session_service.h" #include "chrome/browser/sessions/session_types.h" #include "chrome/browser/sync/glue/favicon_cache.h" #include "chrome/browser/sync/glue/model_associator.h" #include "chrome/browser/sync/glue/synced_session_tracker.h" #include "chrome/browser/sync/glue/tab_node_pool.h" #include "chrome/browser/sync/open_tabs_ui_delegate.h" #include "sync/internal_api/public/base/model_type.h" class PrefServiceSyncable; class Profile; class ProfileSyncService; namespace syncer { class ReadNode; class WriteTransaction; } // namespace syncer namespace sync_pb { class SessionHeader; class SessionSpecifics; class SessionTab; class SessionWindow; class TabNavigation; } // namespace sync_pb namespace browser_sync { class DataTypeErrorHandler; class DeviceInfo; class SyncedTabDelegate; class SyncedWindowDelegate; // Contains all logic for associating the Chrome sessions model and // the sync sessions model. class SessionModelAssociator : public AssociatorInterface, public OpenTabsUIDelegate, public base::SupportsWeakPtr, public base::NonThreadSafe { public: // Does not take ownership of sync_service. SessionModelAssociator(ProfileSyncService* sync_service, DataTypeErrorHandler* error_handler); SessionModelAssociator(ProfileSyncService* sync_service, bool setup_for_test); virtual ~SessionModelAssociator(); // The has_nodes out parameter is set to true if the sync model has // nodes other than the permanent tagged nodes. The method may // return false if an error occurred. virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes) OVERRIDE; // AssociatorInterface implementation. virtual void AbortAssociation() OVERRIDE { // No implementation needed, this associator runs on the main thread. } // See ModelAssociator interface. virtual bool CryptoReadyIfNecessary() OVERRIDE; // Returns sync id for the given session tag. // Returns syncer::kInvalidId if the sync node is not found for the given // tag virtual int64 GetSyncIdFromSessionTag(const std::string& tag); // Resync local window information. Updates the local sessions header node // with the status of open windows and the order of tabs they contain. Should // only be called for changes that affect a window, not a change within a // single tab. // // If |reload_tabs| is true, will also resync all tabs (same as calling // AssociateTabs with a vector of all tabs). // |error| gets set if any association error occurred. // Returns: false if the local session's sync nodes were deleted and // reassociation is necessary, true otherwise. bool AssociateWindows(bool reload_tabs, syncer::SyncError* error); // Loads and reassociates the local tabs referenced in |tabs|. // |error| gets set if any association error occurred. // Returns: false if the local session's sync nodes were deleted and // reassociation is necessary, true otherwise. bool AssociateTabs(const std::vector& tabs, syncer::SyncError* error); // Reassociates a single tab with the sync model. Will check if the tab // already is associated with a sync node and allocate one if necessary. // |error| gets set if any association error occurred. // |tab| will be updated with sync id if necessary. // Returns: false if the local session's sync nodes were deleted and // reassociation is necessary, true otherwise. bool AssociateTab(SyncedTabDelegate* const tab, syncer::SyncError* error); // Load any foreign session info stored in sync db and update the sync db // with local client data. Processes/reuses any sync nodes owned by this // client and creates any further sync nodes needed to store local header and // tab info. virtual syncer::SyncError AssociateModels( syncer::SyncMergeResult* local_merge_result, syncer::SyncMergeResult* syncer_merge_result) OVERRIDE; // Clear local sync data buffers. Does not delete sync nodes to avoid // tombstones. TODO(zea): way to eventually delete orphaned nodes. virtual syncer::SyncError DisassociateModels() OVERRIDE; // Returns the tag used to uniquely identify this machine's session in the // sync model. const std::string& GetCurrentMachineTag() const { DCHECK(!current_machine_tag_.empty()); return current_machine_tag_; } // Load and associate window and tab data for a foreign session. void AssociateForeignSpecifics(const sync_pb::SessionSpecifics& specifics, const base::Time& modification_time); // Removes a foreign session from our internal bookkeeping. // Returns true if the session was found and deleted, false if no data was // found for that session. bool DisassociateForeignSession(const std::string& foreign_session_tag); // Attempts to asynchronously refresh the sessions sync data. If new data is // received, the FOREIGN_SESSIONS_UPDATED notification is sent. No // notification will be sent otherwise. This method is not guaranteed to // trigger a sync cycle. void AttemptSessionsDataRefresh() const; // Sets |*local_session| to point to the associator's representation of the // local machine. Used primarily for testing. bool GetLocalSession(const SyncedSession* * local_session); // Triggers garbage collection of stale sessions (as defined by // |stale_session_threshold_days_|). This is called automatically every // time we start up (via AssociateModels). void DeleteStaleSessions(); // Set the threshold of inactivity (in days) at which we consider sessions // stale. void SetStaleSessionThreshold(size_t stale_session_threshold_days); // Control which local tabs we're interested in syncing. // Ensures the profile matches sync's profile and that the tab has valid // entries. bool ShouldSyncTab(const SyncedTabDelegate& tab) const; // Compare |urls| against |local_tab_map_|'s urls to see if any tabs with // outstanding favicon loads can be fulfilled. void FaviconsUpdated(const std::set& urls); // Returns the syncable model type. static syncer::ModelType model_type() { return syncer::SESSIONS; } // Testing only. Will cause the associator to call MessageLoop::Quit() // when a local change is made, or when timeout occurs, whichever is // first. void BlockUntilLocalChangeForTest(base::TimeDelta timeout); // OpenTabsUIDelegate implementation. virtual bool GetSyncedFaviconForPageURL( const std::string& pageurl, scoped_refptr* favicon_png) const OVERRIDE; virtual bool GetAllForeignSessions( std::vector* sessions) OVERRIDE; virtual bool GetForeignSession( const std::string& tag, std::vector* windows) OVERRIDE; virtual bool GetForeignTab(const std::string& tag, const SessionID::id_type tab_id, const SessionTab** tab) OVERRIDE; virtual void DeleteForeignSession(const std::string& tag) OVERRIDE; void SetCurrentMachineTagForTesting(const std::string& machine_tag) { current_machine_tag_ = machine_tag; } // Gets the device info for a given session tag. scoped_ptr GetDeviceInfoForSessionTag( const std::string& session_tag); FaviconCache* GetFaviconCache(); private: friend class SyncSessionModelAssociatorTest; FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, WriteSessionToNode); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, WriteFilledSessionToNode); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, WriteForeignSessionToNode); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolEmpty); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, ValidTabs); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, ExistingTabs); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, MissingLocalTabNode); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabPoolFreeNodeLimits); FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolDeleteUnassociatedNodes); FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest, PopulateSessionHeader); FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest, PopulateSessionWindow); FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest, PopulateSessionTab); FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest, TabNodePool); // Keep all the links to local tab data in one place. A tab_node_id and tab // must be passed at creation. The tab_node_id is not mutable after, although // all other fields are. class TabLink { public: TabLink(int tab_node_id, const SyncedTabDelegate* tab) : tab_node_id_(tab_node_id), tab_(tab) {} void set_tab(const SyncedTabDelegate* tab) { tab_ = tab; } void set_url(const GURL& url) { url_ = url; } int tab_node_id() const { return tab_node_id_; } const SyncedTabDelegate* tab() const { return tab_; } const GURL& url() const { return url_; } private: DISALLOW_COPY_AND_ASSIGN(TabLink); // The id for the sync node this tab is stored in. const int tab_node_id_; // The tab object itself. const SyncedTabDelegate* tab_; // The currently visible url of the tab (used for syncing favicons). GURL url_; }; // Container for accessing local tab data by tab id. typedef std::map > TabLinksMap; // Determine if a window is of a type we're interested in syncing. static bool ShouldSyncWindow(const SyncedWindowDelegate* window); // Initializes the tag corresponding to this machine. void InitializeCurrentMachineTag(syncer::WriteTransaction* trans); // Updates the server data based upon the current client session. If no node // corresponding to this machine exists in the sync model, one is created. // Returns true on success, false if association failed. bool UpdateSyncModelDataFromClient(syncer::SyncError* error); // Pulls the current sync model from the sync database and returns true upon // update of the client model. Will associate any foreign sessions as well as // keep track of any local tab nodes, adding them to our free tab node pool. bool UpdateAssociationsFromSyncModel(const syncer::ReadNode& root, syncer::WriteTransaction* trans, syncer::SyncError* error); // Return the virtual URL of the current tab, even if it's pending. static GURL GetCurrentVirtualURL(const SyncedTabDelegate& tab_delegate); // Return the favicon url of the current tab, even if it's pending. static GURL GetCurrentFaviconURL(const SyncedTabDelegate& tab_delegate); // Fills a tab sync node with data from a WebContents object. Updates // |tab_link| with the current url if it's valid and triggers a favicon // load if the url has changed. // Returns true on success, false if we need to reassociate due to corruption. bool WriteTabContentsToSyncModel(TabLink* tab_link, syncer::SyncError* error); // Set |session_tab| from |tab_delegate| and |mtime|. static void SetSessionTabFromDelegate( const SyncedTabDelegate& tab_delegate, base::Time mtime, SessionTab* session_tab); // Used to populate a session header from the session specifics header // provided. static void PopulateSessionHeaderFromSpecifics( const sync_pb::SessionHeader& header_specifics, base::Time mtime, SyncedSession* session_header); // Used to populate a session window from the session specifics window // provided. Tracks any foreign session data created through |tracker|. static void PopulateSessionWindowFromSpecifics( const std::string& foreign_session_tag, const sync_pb::SessionWindow& window, base::Time mtime, SessionWindow* session_window, SyncedSessionTracker* tracker); // Helper method to load the favicon data from the tab specifics. If the // favicon is valid, stores the favicon data into the favicon cache. void LoadForeignTabFavicon(const sync_pb::SessionTab& tab); // Returns true if this tab belongs to this profile and belongs to a window, // false otherwise. bool IsValidTab(const SyncedTabDelegate& tab) const; // Having a valid entry is defined as the url being valid and and having a // syncable scheme (non chrome:// and file:// url's). In other words, we don't // want to sync a tab that is nothing but chrome:// and file:// navigations or // invalid url's. bool TabHasValidEntry(const SyncedTabDelegate& tab) const; // Update the tab id of the node associated with |tab_node_id| to // |new_tab_id|. void UpdateTabIdIfNecessary(int tab_node_id, SessionID::id_type new_tab_id); // For testing only. void QuitLoopForSubtleTesting(); // Unique client tag. std::string current_machine_tag_; // User-visible machine name. std::string current_session_name_; // Pool of all used/available sync nodes associated with local tabs. TabNodePool local_tab_pool_; // SyncID for the sync node containing all the window information for this // client. int64 local_session_syncid_; // Mapping of current open (local) tabs to their sync identifiers. TabLinksMap local_tab_map_; SyncedSessionTracker synced_session_tracker_; // Weak pointer. ProfileSyncService* sync_service_; // Number of days without activity after which we consider a session to be // stale and a candidate for garbage collection. size_t stale_session_threshold_days_; // To avoid certain checks not applicable to tests. bool setup_for_test_; // During integration tests, we sometimes need to block until a local change // is made. bool waiting_for_change_; // Profile being synced. Weak pointer. Profile* const profile_; DataTypeErrorHandler* error_handler_; // Our favicon cache. FaviconCache favicon_cache_; base::WeakPtrFactory test_weak_factory_; DISALLOW_COPY_AND_ASSIGN(SessionModelAssociator); }; } // namespace browser_sync #endif // CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_