1 // Copyright (c) 2012 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.
5 #ifndef CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_
6 #define CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_
13 #include "base/basictypes.h"
14 #include "base/compiler_specific.h"
15 #include "base/gtest_prod_util.h"
16 #include "base/memory/linked_ptr.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/threading/non_thread_safe.h"
19 #include "base/time/time.h"
20 #include "chrome/browser/sessions/session_id.h"
21 #include "chrome/browser/sessions/session_service.h"
22 #include "chrome/browser/sessions/session_types.h"
23 #include "chrome/browser/sync/glue/favicon_cache.h"
24 #include "chrome/browser/sync/glue/model_associator.h"
25 #include "chrome/browser/sync/glue/synced_session_tracker.h"
26 #include "chrome/browser/sync/glue/tab_node_pool.h"
27 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
28 #include "sync/internal_api/public/base/model_type.h"
30 class PrefServiceSyncable
;
32 class ProfileSyncService
;
36 class WriteTransaction
;
41 class SessionSpecifics
;
45 } // namespace sync_pb
47 namespace browser_sync
{
49 class DataTypeErrorHandler
;
51 class SyncedTabDelegate
;
52 class SyncedWindowDelegate
;
54 // Contains all logic for associating the Chrome sessions model and
55 // the sync sessions model.
56 class SessionModelAssociator
57 : public AssociatorInterface
,
58 public OpenTabsUIDelegate
,
59 public base::SupportsWeakPtr
<SessionModelAssociator
>,
60 public base::NonThreadSafe
{
62 // Does not take ownership of sync_service.
63 SessionModelAssociator(ProfileSyncService
* sync_service
,
64 DataTypeErrorHandler
* error_handler
);
65 SessionModelAssociator(ProfileSyncService
* sync_service
,
67 virtual ~SessionModelAssociator();
69 // The has_nodes out parameter is set to true if the sync model has
70 // nodes other than the permanent tagged nodes. The method may
71 // return false if an error occurred.
72 virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes
) OVERRIDE
;
74 // AssociatorInterface implementation.
75 virtual void AbortAssociation() OVERRIDE
{
76 // No implementation needed, this associator runs on the main thread.
79 // See ModelAssociator interface.
80 virtual bool CryptoReadyIfNecessary() OVERRIDE
;
82 // Returns sync id for the given session tag.
83 // Returns syncer::kInvalidId if the sync node is not found for the given
85 virtual int64
GetSyncIdFromSessionTag(const std::string
& tag
);
87 // Resync local window information. Updates the local sessions header node
88 // with the status of open windows and the order of tabs they contain. Should
89 // only be called for changes that affect a window, not a change within a
92 // If |reload_tabs| is true, will also resync all tabs (same as calling
93 // AssociateTabs with a vector of all tabs).
94 // |error| gets set if any association error occurred.
95 // Returns: false if the local session's sync nodes were deleted and
96 // reassociation is necessary, true otherwise.
97 bool AssociateWindows(bool reload_tabs
, syncer::SyncError
* error
);
99 // Loads and reassociates the local tabs referenced in |tabs|.
100 // |error| gets set if any association error occurred.
101 // Returns: false if the local session's sync nodes were deleted and
102 // reassociation is necessary, true otherwise.
103 bool AssociateTabs(const std::vector
<SyncedTabDelegate
*>& tabs
,
104 syncer::SyncError
* error
);
106 // Reassociates a single tab with the sync model. Will check if the tab
107 // already is associated with a sync node and allocate one if necessary.
108 // |error| gets set if any association error occurred.
109 // |tab| will be updated with sync id if necessary.
110 // Returns: false if the local session's sync nodes were deleted and
111 // reassociation is necessary, true otherwise.
112 bool AssociateTab(SyncedTabDelegate
* const tab
, syncer::SyncError
* error
);
114 // Load any foreign session info stored in sync db and update the sync db
115 // with local client data. Processes/reuses any sync nodes owned by this
116 // client and creates any further sync nodes needed to store local header and
118 virtual syncer::SyncError
AssociateModels(
119 syncer::SyncMergeResult
* local_merge_result
,
120 syncer::SyncMergeResult
* syncer_merge_result
) OVERRIDE
;
122 // Clear local sync data buffers. Does not delete sync nodes to avoid
123 // tombstones. TODO(zea): way to eventually delete orphaned nodes.
124 virtual syncer::SyncError
DisassociateModels() OVERRIDE
;
126 // Returns the tag used to uniquely identify this machine's session in the
128 const std::string
& GetCurrentMachineTag() const {
129 DCHECK(!current_machine_tag_
.empty());
130 return current_machine_tag_
;
133 // Load and associate window and tab data for a foreign session.
134 void AssociateForeignSpecifics(const sync_pb::SessionSpecifics
& specifics
,
135 const base::Time
& modification_time
);
137 // Removes a foreign session from our internal bookkeeping.
138 // Returns true if the session was found and deleted, false if no data was
139 // found for that session.
140 bool DisassociateForeignSession(const std::string
& foreign_session_tag
);
142 // Attempts to asynchronously refresh the sessions sync data. If new data is
143 // received, the FOREIGN_SESSIONS_UPDATED notification is sent. No
144 // notification will be sent otherwise. This method is not guaranteed to
145 // trigger a sync cycle.
146 void AttemptSessionsDataRefresh() const;
148 // Triggers garbage collection of stale sessions (as defined by
149 // |stale_session_threshold_days_|). This is called automatically every
150 // time we start up (via AssociateModels).
151 void DeleteStaleSessions();
153 // Set the threshold of inactivity (in days) at which we consider sessions
155 void SetStaleSessionThreshold(size_t stale_session_threshold_days
);
157 // Control which local tabs we're interested in syncing.
158 // Ensures the profile matches sync's profile and that the tab has valid
160 bool ShouldSyncTab(const SyncedTabDelegate
& tab
) const;
162 // Compare |urls| against |local_tab_map_|'s urls to see if any tabs with
163 // outstanding favicon loads can be fulfilled.
164 void FaviconsUpdated(const std::set
<GURL
>& urls
);
166 // Returns the syncable model type.
167 static syncer::ModelType
model_type() { return syncer::SESSIONS
; }
169 // Testing only. Will cause the associator to call MessageLoop::Quit()
170 // when a local change is made, or when timeout occurs, whichever is
172 void BlockUntilLocalChangeForTest(base::TimeDelta timeout
);
174 // OpenTabsUIDelegate implementation.
175 virtual bool GetSyncedFaviconForPageURL(
176 const std::string
& pageurl
,
177 scoped_refptr
<base::RefCountedMemory
>* favicon_png
) const OVERRIDE
;
178 virtual bool GetAllForeignSessions(
179 std::vector
<const SyncedSession
*>* sessions
) OVERRIDE
;
180 virtual bool GetForeignSession(
181 const std::string
& tag
,
182 std::vector
<const SessionWindow
*>* windows
) OVERRIDE
;
183 virtual bool GetForeignTab(const std::string
& tag
,
184 const SessionID::id_type tab_id
,
185 const SessionTab
** tab
) OVERRIDE
;
186 virtual void DeleteForeignSession(const std::string
& tag
) OVERRIDE
;
187 virtual bool GetLocalSession(const SyncedSession
* * local_session
) OVERRIDE
;
189 void SetCurrentMachineTagForTesting(const std::string
& machine_tag
) {
190 current_machine_tag_
= machine_tag
;
193 // Gets the device info for a given session tag.
194 scoped_ptr
<browser_sync::DeviceInfo
> GetDeviceInfoForSessionTag(
195 const std::string
& session_tag
);
197 FaviconCache
* GetFaviconCache();
200 friend class SyncSessionModelAssociatorTest
;
201 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
, WriteSessionToNode
);
202 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
,
203 WriteFilledSessionToNode
);
204 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
,
205 WriteForeignSessionToNode
);
206 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
, TabNodePoolEmpty
);
207 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
, TabNodePoolNonEmpty
);
208 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
, ValidTabs
);
209 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
, ExistingTabs
);
210 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
, MissingLocalTabNode
);
211 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
,
212 TabPoolFreeNodeLimits
);
213 FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest
,
214 TabNodePoolDeleteUnassociatedNodes
);
215 FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest
,
216 PopulateSessionHeader
);
217 FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest
,
218 PopulateSessionWindow
);
219 FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest
, PopulateSessionTab
);
220 FRIEND_TEST_ALL_PREFIXES(SyncSessionModelAssociatorTest
,
223 // Keep all the links to local tab data in one place. A tab_node_id and tab
224 // must be passed at creation. The tab_node_id is not mutable after, although
225 // all other fields are.
228 TabLink(int tab_node_id
, const SyncedTabDelegate
* tab
)
229 : tab_node_id_(tab_node_id
),
232 void set_tab(const SyncedTabDelegate
* tab
) { tab_
= tab
; }
233 void set_url(const GURL
& url
) { url_
= url
; }
235 int tab_node_id() const { return tab_node_id_
; }
236 const SyncedTabDelegate
* tab() const { return tab_
; }
237 const GURL
& url() const { return url_
; }
240 DISALLOW_COPY_AND_ASSIGN(TabLink
);
242 // The id for the sync node this tab is stored in.
243 const int tab_node_id_
;
245 // The tab object itself.
246 const SyncedTabDelegate
* tab_
;
248 // The currently visible url of the tab (used for syncing favicons).
252 // Container for accessing local tab data by tab id.
253 typedef std::map
<SessionID::id_type
, linked_ptr
<TabLink
> > TabLinksMap
;
255 // Determine if a window is of a type we're interested in syncing.
256 static bool ShouldSyncWindow(const SyncedWindowDelegate
* window
);
258 // Initializes the tag corresponding to this machine.
259 void InitializeCurrentMachineTag(syncer::WriteTransaction
* trans
);
261 // Updates the server data based upon the current client session. If no node
262 // corresponding to this machine exists in the sync model, one is created.
263 // Returns true on success, false if association failed.
264 bool UpdateSyncModelDataFromClient(syncer::SyncError
* error
);
266 // Pulls the current sync model from the sync database and returns true upon
267 // update of the client model. Will associate any foreign sessions as well as
268 // keep track of any local tab nodes, adding them to our free tab node pool.
269 bool UpdateAssociationsFromSyncModel(const syncer::ReadNode
& root
,
270 syncer::WriteTransaction
* trans
,
271 syncer::SyncError
* error
);
273 // Return the virtual URL of the current tab, even if it's pending.
274 static GURL
GetCurrentVirtualURL(const SyncedTabDelegate
& tab_delegate
);
276 // Return the favicon url of the current tab, even if it's pending.
277 static GURL
GetCurrentFaviconURL(const SyncedTabDelegate
& tab_delegate
);
279 // Fills a tab sync node with data from a WebContents object. Updates
280 // |tab_link| with the current url if it's valid and triggers a favicon
281 // load if the url has changed.
282 // Returns true on success, false if we need to reassociate due to corruption.
283 bool WriteTabContentsToSyncModel(TabLink
* tab_link
,
284 syncer::SyncError
* error
);
286 // Set |session_tab| from |tab_delegate| and |mtime|.
287 static void SetSessionTabFromDelegate(
288 const SyncedTabDelegate
& tab_delegate
,
290 SessionTab
* session_tab
);
292 // Used to populate a session header from the session specifics header
294 static void PopulateSessionHeaderFromSpecifics(
295 const sync_pb::SessionHeader
& header_specifics
,
297 SyncedSession
* session_header
);
299 // Used to populate a session window from the session specifics window
300 // provided. Tracks any foreign session data created through |tracker|.
301 static void PopulateSessionWindowFromSpecifics(
302 const std::string
& foreign_session_tag
,
303 const sync_pb::SessionWindow
& window
,
305 SessionWindow
* session_window
,
306 SyncedSessionTracker
* tracker
);
308 // Helper method to load the favicon data from the tab specifics. If the
309 // favicon is valid, stores the favicon data into the favicon cache.
310 void LoadForeignTabFavicon(const sync_pb::SessionTab
& tab
);
312 // Returns true if this tab belongs to this profile and belongs to a window,
314 bool IsValidTab(const SyncedTabDelegate
& tab
) const;
316 // Having a valid entry is defined as the url being valid and and having a
317 // syncable scheme (non chrome:// and file:// url's). In other words, we don't
318 // want to sync a tab that is nothing but chrome:// and file:// navigations or
320 bool TabHasValidEntry(const SyncedTabDelegate
& tab
) const;
322 // Update the tab id of the node associated with |tab_node_id| to
324 void UpdateTabIdIfNecessary(int tab_node_id
,
325 SessionID::id_type new_tab_id
);
328 void QuitLoopForSubtleTesting();
330 // Unique client tag.
331 std::string current_machine_tag_
;
333 // User-visible machine name.
334 std::string current_session_name_
;
336 // Pool of all used/available sync nodes associated with local tabs.
337 TabNodePool local_tab_pool_
;
339 // SyncID for the sync node containing all the window information for this
341 int64 local_session_syncid_
;
343 // Mapping of current open (local) tabs to their sync identifiers.
344 TabLinksMap local_tab_map_
;
346 SyncedSessionTracker synced_session_tracker_
;
349 ProfileSyncService
* sync_service_
;
351 // Number of days without activity after which we consider a session to be
352 // stale and a candidate for garbage collection.
353 size_t stale_session_threshold_days_
;
355 // To avoid certain checks not applicable to tests.
356 bool setup_for_test_
;
358 // During integration tests, we sometimes need to block until a local change
360 bool waiting_for_change_
;
362 // Profile being synced. Weak pointer.
363 Profile
* const profile_
;
365 DataTypeErrorHandler
* error_handler_
;
367 // Our favicon cache.
368 FaviconCache favicon_cache_
;
370 base::WeakPtrFactory
<SessionModelAssociator
> test_weak_factory_
;
372 DISALLOW_COPY_AND_ASSIGN(SessionModelAssociator
);
375 } // namespace browser_sync
377 #endif // CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_