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. current_machine_tag()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: TabLink(int tab_node_id,const SyncedTabDelegate * tab)156 TabLink(int tab_node_id, const SyncedTabDelegate* tab) 157 : tab_node_id_(tab_node_id), 158 tab_(tab) {} 159 set_tab(const SyncedTabDelegate * tab)160 void set_tab(const SyncedTabDelegate* tab) { tab_ = tab; } set_url(const GURL & url)161 void set_url(const GURL& url) { url_ = url; } 162 tab_node_id()163 int tab_node_id() const { return tab_node_id_; } tab()164 const SyncedTabDelegate* tab() const { return tab_; } url()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