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_BOOKMARK_MODEL_ASSOCIATOR_H_
6 #define CHROME_BROWSER_SYNC_GLUE_BOOKMARK_MODEL_ASSOCIATOR_H_
14 #include "base/basictypes.h"
15 #include "base/compiler_specific.h"
16 #include "base/hash.h"
17 #include "base/memory/weak_ptr.h"
18 #include "components/sync_driver/data_type_error_handler.h"
19 #include "components/sync_driver/model_associator.h"
20 #include "sync/internal_api/public/util/unrecoverable_error_handler.h"
32 class BaseTransaction
;
34 class WriteTransaction
;
37 namespace browser_sync
{
39 // Contains all model association related logic:
40 // * Algorithm to associate bookmark model and sync model.
41 // * Methods to get a bookmark node for a given sync node and vice versa.
42 // * Persisting model associations and loading them back.
43 class BookmarkModelAssociator
44 : public sync_driver::
45 PerDataTypeAssociatorInterface
<bookmarks::BookmarkNode
, int64
> {
47 static syncer::ModelType
model_type() { return syncer::BOOKMARKS
; }
48 // |expect_mobile_bookmarks_folder| controls whether or not we
49 // expect the mobile bookmarks permanent folder to be created.
50 // Should be set to true only by mobile clients.
51 BookmarkModelAssociator(
52 bookmarks::BookmarkModel
* bookmark_model
,
54 syncer::UserShare
* user_share
,
55 sync_driver::DataTypeErrorHandler
* unrecoverable_error_handler
,
56 bool expect_mobile_bookmarks_folder
);
57 ~BookmarkModelAssociator() override
;
59 // Updates the visibility of the permanents node in the BookmarkModel.
60 void UpdatePermanentNodeVisibility();
62 // AssociatorInterface implementation.
64 // AssociateModels iterates through both the sync and the browser
65 // bookmark model, looking for matched pairs of items. For any pairs it
66 // finds, it will call AssociateSyncID. For any unmatched items,
67 // MergeAndAssociateModels will try to repair the match, e.g. by adding a new
68 // node. After successful completion, the models should be identical and
69 // corresponding. Returns true on success. On failure of this step, we
70 // should abort the sync operation and report an error to the user.
71 syncer::SyncError
AssociateModels(
72 syncer::SyncMergeResult
* local_merge_result
,
73 syncer::SyncMergeResult
* syncer_merge_result
) override
;
75 syncer::SyncError
DisassociateModels() override
;
77 // The has_nodes out param is true if the sync model has nodes other
78 // than the permanent tagged nodes.
79 bool SyncModelHasUserCreatedNodes(bool* has_nodes
) override
;
81 // Returns sync id for the given bookmark node id.
82 // Returns syncer::kInvalidId if the sync node is not found for the given
84 int64
GetSyncIdFromChromeId(const int64
& node_id
) override
;
86 // Returns the bookmark node for the given sync id.
87 // Returns NULL if no bookmark node is found for the given sync id.
88 const bookmarks::BookmarkNode
* GetChromeNodeFromSyncId(
89 int64 sync_id
) override
;
91 // Initializes the given sync node from the given bookmark node id.
92 // Returns false if no sync node was found for the given bookmark node id or
93 // if the initialization of sync node fails.
94 bool InitSyncNodeFromChromeId(const int64
& node_id
,
95 syncer::BaseNode
* sync_node
) override
;
97 // Associates the given bookmark node with the given sync node.
98 void Associate(const bookmarks::BookmarkNode
* node
,
99 const syncer::BaseNode
& sync_node
) override
;
100 // Remove the association that corresponds to the given sync id.
101 void Disassociate(int64 sync_id
) override
;
103 void AbortAssociation() override
{
104 // No implementation needed, this associator runs on the main
108 // See ModelAssociator interface.
109 bool CryptoReadyIfNecessary() override
;
112 typedef std::map
<int64
, int64
> BookmarkIdToSyncIdMap
;
113 typedef std::map
<int64
, const bookmarks::BookmarkNode
*>
114 SyncIdToBookmarkNodeMap
;
115 typedef std::set
<int64
> DirtyAssociationsSyncIds
;
116 typedef std::vector
<const bookmarks::BookmarkNode
*> BookmarkList
;
117 typedef std::stack
<const bookmarks::BookmarkNode
*> BookmarkStack
;
119 // Add association between native node and sync node to the maps.
120 void AddAssociation(const bookmarks::BookmarkNode
* node
, int64 sync_id
);
122 // Posts a task to persist dirty associations.
123 void PostPersistAssociationsTask();
124 // Persists all dirty associations.
125 void PersistAssociations();
127 // Result of the native model version check against the sync
128 // version performed by CheckModelSyncState.
129 enum NativeModelSyncState
{
130 // The native version is syncer::syncable::kInvalidTransactionVersion,
131 // which is the case when the version has either not been set yet or reset
132 // as a result of a previous error during the association. Basically the
133 // state should return back to UNSET on an association following the one
134 // where the state was different than IN_SYNC.
136 // The native version was in sync with the Sync version.
138 // The native version was behing the sync version which indicates a failure
139 // to persist the native bookmarks model.
141 // The native version was ahead of the sync version which indicates a
142 // a failure to persist Sync DB.
144 NATIVE_MODEL_SYNC_STATE_COUNT
147 // Helper class used within AssociateModels to simplify the logic and
148 // minimize the number of arguments passed between private functions.
151 Context(syncer::SyncMergeResult
* local_merge_result
,
152 syncer::SyncMergeResult
* syncer_merge_result
);
155 // Push a sync node to the DFS stack.
156 void PushNode(int64 sync_id
);
157 // Pops a sync node from the DFS stack. Returns false if the stack
159 bool PopNode(int64
* sync_id
);
161 // The following methods are used to update |local_merge_result_| and
162 // |syncer_merge_result_|.
163 void SetPreAssociationVersions(int64 native_version
, int64 sync_version
);
164 void SetNumItemsBeforeAssociation(int local_num
, int sync_num
);
165 void SetNumItemsAfterAssociation(int local_num
, int sync_num
);
166 void IncrementLocalItemsDeleted();
167 void IncrementLocalItemsAdded();
168 void IncrementLocalItemsModified();
169 void IncrementSyncItemsAdded();
170 void IncrementSyncItemsDeleted(int count
);
172 void UpdateDuplicateCount(const base::string16
& title
, const GURL
& url
);
174 int duplicate_count() const { return duplicate_count_
; }
175 NativeModelSyncState
native_model_sync_state() const {
176 return native_model_sync_state_
;
178 void set_native_model_sync_state(NativeModelSyncState state
) {
179 native_model_sync_state_
= state
;
182 // Bookmark roots participating in the sync.
183 void AddBookmarkRoot(const bookmarks::BookmarkNode
* root
);
184 const BookmarkList
& bookmark_roots() const { return bookmark_roots_
; }
186 // Index of local bookmark nodes by native ID.
187 const bookmarks::BookmarkNode
* LookupNodeInIdIndex(int64 native_id
);
189 void MarkForVersionUpdate(const bookmarks::BookmarkNode
* node
);
190 const BookmarkList
& bookmarks_for_version_update() const {
191 return bookmarks_for_version_update_
;
195 // DFS stack of sync nodes traversed during association.
196 std::stack
<int64
> dfs_stack_
;
197 // Local and merge results are not owned.
198 syncer::SyncMergeResult
* local_merge_result_
;
199 syncer::SyncMergeResult
* syncer_merge_result_
;
200 // |hashes_| contains hash codes of all native bookmarks
201 // for the purpose of detecting duplicates. A small number of
202 // false positives due to hash collisions is OK because this
203 // data is used for reporting purposes only.
204 base::hash_set
<size_t> hashes_
;
205 // Overall number of bookmark collisions from RecordDuplicates call.
206 int duplicate_count_
;
207 // Result of the most recent BookmarkModelAssociator::CheckModelSyncState.
208 NativeModelSyncState native_model_sync_state_
;
209 // List of bookmark model roots participating in the sync.
210 BookmarkList bookmark_roots_
;
211 // Map of bookmark nodes by native ID. Used to lookup sync node matches
213 typedef base::hash_map
<int64
, const bookmarks::BookmarkNode
*> IdIndex
;
215 bool id_index_initialized_
;
216 // List of bookmark nodes for which the transaction version needs to be
218 BookmarkList bookmarks_for_version_update_
;
222 DISALLOW_COPY_AND_ASSIGN(Context
);
225 // Matches up the bookmark model and the sync model to build model
227 syncer::SyncError
BuildAssociations(Context
* context
);
229 // Two helper functions that populate SyncMergeResult with numbers of
230 // items before/after the association.
231 void SetNumItemsBeforeAssociation(syncer::BaseTransaction
* trans
,
233 void SetNumItemsAfterAssociation(syncer::BaseTransaction
* trans
,
236 // Used by SetNumItemsBeforeAssociation.
237 // Similar to BookmarkNode::GetTotalNodeCount but also scans the native
238 // model for duplicates and records them in |context|.
239 int GetTotalBookmarkCountAndRecordDuplicates(
240 const bookmarks::BookmarkNode
* node
,
241 Context
* context
) const;
243 // Helper function that associates all tagged permanent folders and primes
244 // the provided context with sync IDs of those folders.
245 syncer::SyncError
AssociatePermanentFolders(syncer::BaseTransaction
* trans
,
248 // Associate a top-level node of the bookmark model with a permanent node in
249 // the sync domain. Such permanent nodes are identified by a tag that is
250 // well known to the server and the client, and is unique within a particular
251 // user's share. For example, "other_bookmarks" is the tag for the Other
252 // Bookmarks folder. The sync nodes are server-created.
253 // Returns true on success, false if association failed.
254 bool AssociateTaggedPermanentNode(
255 syncer::BaseTransaction
* trans
,
256 const bookmarks::BookmarkNode
* permanent_node
,
257 const std::string
& tag
) WARN_UNUSED_RESULT
;
259 // Removes bookmark nodes whose corresponding sync nodes have been deleted
260 // according to sync delete journals.
261 void ApplyDeletesFromSyncJournal(syncer::BaseTransaction
* trans
,
264 // The main part of the association process that associatiates
265 // native nodes that are children of |parent_node| with sync nodes with IDs
267 syncer::SyncError
BuildAssociations(
268 syncer::WriteTransaction
* trans
,
269 const bookmarks::BookmarkNode
* parent_node
,
270 const std::vector
<int64
>& sync_ids
,
273 // This is a variation of BuildAssociations method above for the optimistic
274 // case where the native version of the storage is in sync or ahead of the
276 syncer::SyncError
BuildAssociationsOptimistic(
277 syncer::WriteTransaction
* trans
,
278 const bookmarks::BookmarkNode
* parent_node
,
279 const std::vector
<int64
>& sync_ids
,
282 // Helper method for creating a new native bookmark node.
283 const bookmarks::BookmarkNode
* CreateBookmarkNode(
284 const bookmarks::BookmarkNode
* parent_node
,
286 const syncer::BaseNode
* sync_child_node
,
289 syncer::SyncError
* error
);
291 // Helper method for deleting a sync node and all its children.
292 // Returns the number of sync nodes deleted.
293 int RemoveSyncNodeHierarchy(syncer::WriteTransaction
* trans
, int64 sync_id
);
295 // Check whether bookmark model and sync model are synced by comparing
296 // their transaction versions.
297 // Returns a PERSISTENCE_ERROR if a transaction mismatch was detected where
298 // the native model has a newer transaction verison.
299 syncer::SyncError
CheckModelSyncState(Context
* context
) const;
301 bookmarks::BookmarkModel
* bookmark_model_
;
303 syncer::UserShare
* user_share_
;
304 sync_driver::DataTypeErrorHandler
* unrecoverable_error_handler_
;
305 const bool expect_mobile_bookmarks_folder_
;
306 BookmarkIdToSyncIdMap id_map_
;
307 SyncIdToBookmarkNodeMap id_map_inverse_
;
308 // Stores sync ids for dirty associations.
309 DirtyAssociationsSyncIds dirty_associations_sync_ids_
;
310 // Specifies whether optimistic association experiment is enabled.
311 bool optimistic_association_enabled_
;
313 // Used to post PersistAssociation tasks to the current message loop and
314 // guarantees no invocations can occur if |this| has been deleted. (This
315 // allows this class to be non-refcounted).
316 base::WeakPtrFactory
<BookmarkModelAssociator
> weak_factory_
;
318 DISALLOW_COPY_AND_ASSIGN(BookmarkModelAssociator
);
321 } // namespace browser_sync
323 #endif // CHROME_BROWSER_SYNC_GLUE_BOOKMARK_MODEL_ASSOCIATOR_H_