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