Home | History | Annotate | Download | only in sessions
      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
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_BROWSER_SYNC_SESSIONS_SESSIONS_SYNC_MANAGER_H_
      6 #define CHROME_BROWSER_SYNC_SESSIONS_SESSIONS_SYNC_MANAGER_H_
      7 
      8 #include <map>
      9 #include <set>
     10 #include <string>
     11 #include <vector>
     12 
     13 #include "base/basictypes.h"
     14 #include "base/gtest_prod_util.h"
     15 #include "base/memory/scoped_vector.h"
     16 #include "base/memory/weak_ptr.h"
     17 #include "base/time/time.h"
     18 #include "chrome/browser/sessions/session_types.h"
     19 #include "chrome/browser/sync/glue/favicon_cache.h"
     20 #include "chrome/browser/sync/glue/synced_session.h"
     21 #include "chrome/browser/sync/glue/synced_session_tracker.h"
     22 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
     23 #include "chrome/browser/sync/sessions/tab_node_pool.h"
     24 #include "components/sessions/session_id.h"
     25 #include "components/sync_driver/device_info.h"
     26 #include "components/sync_driver/sync_prefs.h"
     27 #include "sync/api/syncable_service.h"
     28 
     29 class Profile;
     30 
     31 namespace syncer {
     32 class SyncErrorFactory;
     33 }
     34 
     35 namespace sync_driver {
     36 class LocalDeviceInfoProvider;
     37 }
     38 
     39 namespace sync_pb {
     40 class SessionHeader;
     41 class SessionSpecifics;
     42 class SessionTab;
     43 class SessionWindow;
     44 class TabNavigation;
     45 }  // namespace sync_pb
     46 
     47 namespace browser_sync {
     48 
     49 class DataTypeErrorHandler;
     50 class SyncedTabDelegate;
     51 class SyncedWindowDelegate;
     52 class SyncedWindowDelegatesGetter;
     53 
     54 // An interface defining the ways in which local open tab events can interact
     55 // with session sync.  All local tab events flow to sync via this interface.
     56 // In that way it is analogous to sync changes flowing to the local model
     57 // via ProcessSyncChanges, just with a more granular breakdown.
     58 class LocalSessionEventHandler {
     59  public:
     60   // A local navigation event took place that affects the synced session
     61   // for this instance of Chrome.
     62   virtual void OnLocalTabModified(SyncedTabDelegate* modified_tab) = 0;
     63 
     64   // A local navigation occurred that triggered updates to favicon data for
     65   // each URL in |updated_page_urls|.  This is routed through Sessions Sync so
     66   // that we can filter (exclude) favicon updates for pages that aren't
     67   // currently part of the set of local open tabs, and pass relevant updates
     68   // on to FaviconCache for out-of-band favicon syncing.
     69   virtual void OnFaviconPageUrlsUpdated(
     70       const std::set<GURL>& updated_page_urls) = 0;
     71 };
     72 
     73 // The LocalSessionEventRouter is responsible for hooking itself up to various
     74 // notification sources in the browser process and forwarding relevant
     75 // events to a handler as defined in the LocalSessionEventHandler contract.
     76 class LocalSessionEventRouter {
     77  public:
     78   virtual ~LocalSessionEventRouter();
     79   virtual void StartRoutingTo(LocalSessionEventHandler* handler) = 0;
     80   virtual void Stop() = 0;
     81 };
     82 
     83 // Contains all logic for associating the Chrome sessions model and
     84 // the sync sessions model.
     85 class SessionsSyncManager : public syncer::SyncableService,
     86                             public OpenTabsUIDelegate,
     87                             public LocalSessionEventHandler {
     88  public:
     89   SessionsSyncManager(Profile* profile,
     90                       sync_driver::LocalDeviceInfoProvider* local_device,
     91                       scoped_ptr<LocalSessionEventRouter> router);
     92   virtual ~SessionsSyncManager();
     93 
     94   // syncer::SyncableService implementation.
     95   virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
     96       syncer::ModelType type,
     97       const syncer::SyncDataList& initial_sync_data,
     98       scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
     99       scoped_ptr<syncer::SyncErrorFactory> error_handler) OVERRIDE;
    100   virtual void StopSyncing(syncer::ModelType type) OVERRIDE;
    101   virtual syncer::SyncDataList GetAllSyncData(
    102       syncer::ModelType type) const OVERRIDE;
    103   virtual syncer::SyncError ProcessSyncChanges(
    104       const tracked_objects::Location& from_here,
    105       const syncer::SyncChangeList& change_list) OVERRIDE;
    106 
    107   // OpenTabsUIDelegate implementation.
    108   virtual bool GetSyncedFaviconForPageURL(
    109       const std::string& pageurl,
    110       scoped_refptr<base::RefCountedMemory>* favicon_png) const OVERRIDE;
    111   virtual bool GetAllForeignSessions(
    112       std::vector<const SyncedSession*>* sessions) OVERRIDE;
    113   virtual bool GetForeignSession(
    114       const std::string& tag,
    115       std::vector<const SessionWindow*>* windows) OVERRIDE;
    116   virtual bool GetForeignTab(const std::string& tag,
    117                              const SessionID::id_type tab_id,
    118                              const SessionTab** tab) OVERRIDE;
    119   virtual void DeleteForeignSession(const std::string& tag) OVERRIDE;
    120   virtual bool GetLocalSession(const SyncedSession* * local_session) OVERRIDE;
    121 
    122   // LocalSessionEventHandler implementation.
    123   virtual void OnLocalTabModified(SyncedTabDelegate* modified_tab) OVERRIDE;
    124   virtual void OnFaviconPageUrlsUpdated(
    125       const std::set<GURL>& updated_favicon_page_urls) OVERRIDE;
    126 
    127   // Returns the tag used to uniquely identify this machine's session in the
    128   // sync model.
    129   const std::string& current_machine_tag() const {
    130     DCHECK(!current_machine_tag_.empty());
    131     return current_machine_tag_;
    132   }
    133 
    134   // Return the virtual URL of the current tab, even if it's pending.
    135   static GURL GetCurrentVirtualURL(const SyncedTabDelegate& tab_delegate);
    136 
    137   // Return the favicon url of the current tab, even if it's pending.
    138   static GURL GetCurrentFaviconURL(const SyncedTabDelegate& tab_delegate);
    139 
    140   FaviconCache* GetFaviconCache();
    141 
    142   SyncedWindowDelegatesGetter* GetSyncedWindowDelegatesGetter() const;
    143 
    144   // Triggers garbage collection of stale sessions (as defined by
    145   // |stale_session_threshold_days_|). This is called automatically every
    146   // time we start up (via AssociateModels) and when new sessions data is
    147   // downloaded (sync cycles complete).
    148   void DoGarbageCollection();
    149 
    150  private:
    151   // Keep all the links to local tab data in one place. A tab_node_id and tab
    152   // must be passed at creation. The tab_node_id is not mutable, although
    153   // all other fields are.
    154   class TabLink {
    155    public:
    156     TabLink(int tab_node_id, const SyncedTabDelegate* tab)
    157       : tab_node_id_(tab_node_id),
    158         tab_(tab) {}
    159 
    160     void set_tab(const SyncedTabDelegate* tab) { tab_ = tab; }
    161     void set_url(const GURL& url) { url_ = url; }
    162 
    163     int tab_node_id() const { return tab_node_id_; }
    164     const SyncedTabDelegate* tab() const { return tab_; }
    165     const GURL& url() const { return url_; }
    166 
    167    private:
    168     // The id for the sync node this tab is stored in.
    169     const int tab_node_id_;
    170 
    171     // The tab object itself.
    172     const SyncedTabDelegate* tab_;
    173 
    174     // The currently visible url of the tab (used for syncing favicons).
    175     GURL url_;
    176 
    177     DISALLOW_COPY_AND_ASSIGN(TabLink);
    178   };
    179 
    180   // Container for accessing local tab data by tab id.
    181   typedef std::map<SessionID::id_type, linked_ptr<TabLink> > TabLinksMap;
    182 
    183   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, PopulateSessionHeader);
    184   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, PopulateSessionWindow);
    185   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, ValidTabs);
    186   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, SetSessionTabFromDelegate);
    187   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    188                            SetSessionTabFromDelegateNavigationIndex);
    189   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    190                            SetSessionTabFromDelegateCurrentInvalid);
    191   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, BlockedNavigations);
    192   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, DeleteForeignSession);
    193   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    194                            SaveUnassociatedNodesForReassociation);
    195   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest, MergeDeletesCorruptNode);
    196   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    197                            MergeLocalSessionExistingTabs);
    198   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    199                            CheckPrerenderedWebContentsSwap);
    200   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    201                            AssociateWindowsDontReloadTabs);
    202   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    203                            SwappedOutOnRestore);
    204   FRIEND_TEST_ALL_PREFIXES(SessionsSyncManagerTest,
    205                            ProcessRemoteDeleteOfLocalSession);
    206 
    207   void InitializeCurrentMachineTag();
    208 
    209   // Load and add window or tab data for a foreign session to our internal
    210   // tracking.
    211   void UpdateTrackerWithForeignSession(
    212       const sync_pb::SessionSpecifics& specifics,
    213       const base::Time& modification_time);
    214 
    215   // Returns true if |sync_data| contained a header node for the current
    216   // machine, false otherwise. |restored_tabs| is a filtered tab-only
    217   // subset of |sync_data| returned by this function for convenience.
    218   // |new_changes| is a link to the SyncChange pipeline that exists in the
    219   // caller's context. This function will append necessary changes for
    220   // processing later.
    221   bool InitFromSyncModel(const syncer::SyncDataList& sync_data,
    222                          syncer::SyncDataList* restored_tabs,
    223                          syncer::SyncChangeList* new_changes);
    224 
    225   // Helper to construct a deletion SyncChange for a *tab node*.
    226   // Caller should check IsValid() on the returned change, as it's possible
    227   // this node could not be deleted.
    228   syncer::SyncChange TombstoneTab(const sync_pb::SessionSpecifics& tab);
    229 
    230   // Helper method to load the favicon data from the tab specifics. If the
    231   // favicon is valid, stores the favicon data into the favicon cache.
    232   void RefreshFaviconVisitTimesFromForeignTab(
    233       const sync_pb::SessionTab& tab, const base::Time& modification_time);
    234 
    235   // Removes a foreign session from our internal bookkeeping.
    236   // Returns true if the session was found and deleted, false if no data was
    237   // found for that session.  This will *NOT* trigger sync deletions. See
    238   // DeleteForeignSession below.
    239   bool DisassociateForeignSession(const std::string& foreign_session_tag);
    240 
    241   // Delete a foreign session and all its sync data.
    242   // |change_output| *must* be provided as a link to the SyncChange pipeline
    243   // that exists in the caller's context. This function will append necessary
    244   // changes for processing later.
    245   void DeleteForeignSessionInternal(const std::string& tag,
    246                                     syncer::SyncChangeList* change_output);
    247 
    248   // Used to populate a session header from the session specifics header
    249   // provided.
    250   static void PopulateSessionHeaderFromSpecifics(
    251       const sync_pb::SessionHeader& header_specifics,
    252       base::Time mtime,
    253       SyncedSession* session_header);
    254 
    255   // Builds |session_window| from the session specifics window
    256   // provided and updates the SessionTracker with foreign session data created.
    257   void BuildSyncedSessionFromSpecifics(
    258       const std::string& session_tag,
    259       const sync_pb::SessionWindow& specifics,
    260       base::Time mtime,
    261       SessionWindow* session_window);
    262 
    263   // Resync local window information. Updates the local sessions header node
    264   // with the status of open windows and the order of tabs they contain. Should
    265   // only be called for changes that affect a window, not a change within a
    266   // single tab.
    267   //
    268   // RELOAD_TABS will additionally cause a resync of all tabs (same as calling
    269   // AssociateTabs with a vector of all tabs).
    270   //
    271   // |restored_tabs| is a filtered tab-only subset of initial sync data, if
    272   // available (during MergeDataAndStartSyncing). It can be used to obtain
    273   // baseline SessionSpecifics for tabs we can't fully associate any other
    274   // way because they don't yet have a WebContents.
    275   //
    276   // Returns: false if the local session's sync nodes were deleted and
    277   // reassociation is necessary, true otherwise.
    278   //
    279   // |change_output| *must* be provided as a link to the SyncChange pipeline
    280   // that exists in the caller's context. This function will append necessary
    281   // changes for processing later.
    282   enum ReloadTabsOption {
    283     RELOAD_TABS,
    284     DONT_RELOAD_TABS
    285   };
    286   void AssociateWindows(ReloadTabsOption option,
    287                         const syncer::SyncDataList& restored_tabs,
    288                         syncer::SyncChangeList* change_output);
    289 
    290   // Loads and reassociates the local tabs referenced in |tabs|.
    291   // |change_output| *must* be provided as a link to the SyncChange pipeline
    292   // that exists in the caller's context. This function will append necessary
    293   // changes for processing later.
    294   void AssociateTab(SyncedTabDelegate* const tab,
    295                     syncer::SyncChangeList* change_output);
    296 
    297   // Set |session_tab| from |tab_delegate| and |mtime|.
    298   static void SetSessionTabFromDelegate(
    299       const SyncedTabDelegate& tab_delegate,
    300       base::Time mtime,
    301       SessionTab* session_tab);
    302 
    303   // Populates |specifics| based on the data in |tab_delegate|.
    304   void LocalTabDelegateToSpecifics(const SyncedTabDelegate& tab_delegate,
    305                                    sync_pb::SessionSpecifics* specifics);
    306 
    307   // It's possible that when we associate windows, tabs aren't all loaded
    308   // into memory yet (e.g on android) and we don't have a WebContents. In this
    309   // case we can't do a full association, but we still want to update tab IDs
    310   // as they may have changed after a session was restored.  This method
    311   // compares new_tab_id against the previously persisted tab ID (from
    312   // our TabNodePool) and updates it if it differs.
    313   // |restored_tabs| is a filtered tab-only subset of initial sync data, if
    314   // available (during MergeDataAndStartSyncing). It can be used to obtain
    315   // baseline SessionSpecifics for tabs we can't fully associate any other
    316   // way because they don't yet have a WebContents.
    317   // TODO(tim): Bug 98892. We should be able to test this for this on android
    318   // even though we didn't have tests for old API-based sessions sync.
    319   void AssociateRestoredPlaceholderTab(
    320       const SyncedTabDelegate& tab_delegate,
    321       SessionID::id_type new_tab_id,
    322       const syncer::SyncDataList& restored_tabs,
    323       syncer::SyncChangeList* change_output);
    324 
    325   // Stops and re-starts syncing to rebuild association mappings.
    326   // See |local_tab_pool_out_of_sync_|.
    327   void RebuildAssociations();
    328 
    329   // Validates the content of a SessionHeader protobuf.
    330   // Returns false if validation fails.
    331   static bool IsValidSessionHeader(const sync_pb::SessionHeader& header);
    332 
    333   // Mapping of current open (local) tabs to their sync identifiers.
    334   TabLinksMap local_tab_map_;
    335 
    336   SyncedSessionTracker session_tracker_;
    337   FaviconCache favicon_cache_;
    338 
    339   // Pool of used/available sync nodes associated with local tabs.
    340   TabNodePool local_tab_pool_;
    341 
    342   // Tracks whether our local representation of which sync nodes map to what
    343   // tabs (belonging to the current local session) is inconsistent.  This can
    344   // happen if a foreign client deems our session as "stale" and decides to
    345   // delete it. Rather than respond by bullishly re-creating our nodes
    346   // immediately, which could lead to ping-pong sequences, we give the benefit
    347   // of the doubt and hold off until another local navigation occurs, which
    348   // proves that we are still relevant.
    349   bool local_tab_pool_out_of_sync_;
    350 
    351   sync_driver::SyncPrefs sync_prefs_;
    352 
    353   const Profile* const profile_;
    354 
    355   scoped_ptr<syncer::SyncErrorFactory> error_handler_;
    356   scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
    357 
    358   // Local device info provider, owned by ProfileSyncService.
    359   const sync_driver::LocalDeviceInfoProvider* const local_device_;
    360 
    361   // Unique client tag.
    362   std::string current_machine_tag_;
    363 
    364   // User-visible machine name.
    365   std::string current_session_name_;
    366 
    367   // SyncID for the sync node containing all the window information for this
    368   // client.
    369   int local_session_header_node_id_;
    370 
    371   // Number of days without activity after which we consider a session to be
    372   // stale and a candidate for garbage collection.
    373   size_t stale_session_threshold_days_;
    374 
    375   scoped_ptr<LocalSessionEventRouter> local_event_router_;
    376   scoped_ptr<SyncedWindowDelegatesGetter> synced_window_getter_;
    377 
    378   DISALLOW_COPY_AND_ASSIGN(SessionsSyncManager);
    379 };
    380 
    381 }  // namespace browser_sync
    382 
    383 #endif  // CHROME_BROWSER_SYNC_SESSIONS_SESSIONS_SYNC_MANAGER_H_
    384