summaryrefslogtreecommitdiffstats
path: root/components/sync_bookmarks/bookmark_model_associator.h
blob: f10e8d4927cbc3f969b10961fa6107ebdf6ff213 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_ASSOCIATOR_H_
#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_ASSOCIATOR_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <set>
#include <stack>
#include <string>
#include <vector>

#include "base/compiler_specific.h"
#include "base/containers/hash_tables.h"
#include "base/hash.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "base/threading/thread_checker.h"
#include "components/sync_driver/data_type_error_handler.h"
#include "components/sync_driver/model_associator.h"
#include "sync/internal_api/public/util/unrecoverable_error_handler.h"

class GURL;

namespace bookmarks {
class BookmarkModel;
class BookmarkNode;
}

namespace syncer {
class BaseNode;
class BaseTransaction;
struct UserShare;
class WriteTransaction;
}

namespace sync_driver {
class SyncClient;
}

namespace browser_sync {

// Contains all model association related logic:
// * Algorithm to associate bookmark model and sync model.
// * Methods to get a bookmark node for a given sync node and vice versa.
// * Persisting model associations and loading them back.
class BookmarkModelAssociator
    : public sync_driver::
          PerDataTypeAssociatorInterface<bookmarks::BookmarkNode, int64_t> {
 public:
  static syncer::ModelType model_type() { return syncer::BOOKMARKS; }
  // |expect_mobile_bookmarks_folder| controls whether or not we
  // expect the mobile bookmarks permanent folder to be created.
  // Should be set to true only by mobile clients.
  BookmarkModelAssociator(
      bookmarks::BookmarkModel* bookmark_model,
      sync_driver::SyncClient* sync_client,
      syncer::UserShare* user_share,
      sync_driver::DataTypeErrorHandler* unrecoverable_error_handler,
      bool expect_mobile_bookmarks_folder);
  ~BookmarkModelAssociator() override;

  // AssociatorInterface implementation.
  //
  // AssociateModels iterates through both the sync and the browser
  // bookmark model, looking for matched pairs of items.  For any pairs it
  // finds, it will call AssociateSyncID.  For any unmatched items,
  // MergeAndAssociateModels will try to repair the match, e.g. by adding a new
  // node.  After successful completion, the models should be identical and
  // corresponding. Returns true on success.  On failure of this step, we
  // should abort the sync operation and report an error to the user.
  syncer::SyncError AssociateModels(
      syncer::SyncMergeResult* local_merge_result,
      syncer::SyncMergeResult* syncer_merge_result) override;

  syncer::SyncError DisassociateModels() override;

  // The has_nodes out param is true if the sync model has nodes other
  // than the permanent tagged nodes.
  bool SyncModelHasUserCreatedNodes(bool* has_nodes) override;

  // Returns sync id for the given bookmark node id.
  // Returns syncer::kInvalidId if the sync node is not found for the given
  // bookmark node id.
  int64_t GetSyncIdFromChromeId(const int64_t& node_id) override;

  // Returns the bookmark node for the given sync id.
  // Returns NULL if no bookmark node is found for the given sync id.
  const bookmarks::BookmarkNode* GetChromeNodeFromSyncId(
      int64_t sync_id) override;

  // Initializes the given sync node from the given bookmark node id.
  // Returns false if no sync node was found for the given bookmark node id or
  // if the initialization of sync node fails.
  bool InitSyncNodeFromChromeId(const int64_t& node_id,
                                syncer::BaseNode* sync_node) override;

  // Associates the given bookmark node with the given sync node.
  void Associate(const bookmarks::BookmarkNode* node,
                 const syncer::BaseNode& sync_node) override;
  // Remove the association that corresponds to the given sync id.
  void Disassociate(int64_t sync_id) override;

  void AbortAssociation() override {
    // No implementation needed, this associator runs on the main
    // thread.
  }

  // See ModelAssociator interface.
  bool CryptoReadyIfNecessary() override;

 private:
  typedef std::map<int64_t, int64_t> BookmarkIdToSyncIdMap;
  typedef std::map<int64_t, const bookmarks::BookmarkNode*>
      SyncIdToBookmarkNodeMap;
  typedef std::set<int64_t> DirtyAssociationsSyncIds;
  typedef std::vector<const bookmarks::BookmarkNode*> BookmarkList;
  typedef std::stack<const bookmarks::BookmarkNode*> BookmarkStack;

  // Add association between native node and sync node to the maps.
  void AddAssociation(const bookmarks::BookmarkNode* node, int64_t sync_id);

  // Posts a task to persist dirty associations.
  void PostPersistAssociationsTask();
  // Persists all dirty associations.
  void PersistAssociations();

  // Result of the native model version check against the sync
  // version performed by CheckModelSyncState.
  enum NativeModelSyncState {
    // The native version is syncer::syncable::kInvalidTransactionVersion,
    // which is the case when the version has either not been set yet or reset
    // as a result of a previous error during the association. Basically the
    // state should return back to UNSET on an association following the one
    // where the state was different than IN_SYNC.
    UNSET,
    // The native version was in sync with the Sync version.
    IN_SYNC,
    // The native version was behing the sync version which indicates a failure
    // to persist the native bookmarks model.
    BEHIND,
    // The native version was ahead of the sync version which indicates a
    // a failure to persist Sync DB.
    AHEAD,
    NATIVE_MODEL_SYNC_STATE_COUNT
  };

  // Helper class used within AssociateModels to simplify the logic and
  // minimize the number of arguments passed between private functions.
  class Context {
   public:
    Context(syncer::SyncMergeResult* local_merge_result,
            syncer::SyncMergeResult* syncer_merge_result);
    ~Context();

    // Push a sync node to the DFS stack.
    void PushNode(int64_t sync_id);
    // Pops a sync node from the DFS stack. Returns false if the stack
    // is empty.
    bool PopNode(int64_t* sync_id);

    // The following methods are used to update |local_merge_result_| and
    // |syncer_merge_result_|.
    void SetPreAssociationVersions(int64_t native_version,
                                   int64_t sync_version);
    void SetNumItemsBeforeAssociation(int local_num, int sync_num);
    void SetNumItemsAfterAssociation(int local_num, int sync_num);
    void IncrementLocalItemsDeleted();
    void IncrementLocalItemsAdded();
    void IncrementLocalItemsModified();
    void IncrementSyncItemsAdded();
    void IncrementSyncItemsDeleted(int count);

    void UpdateDuplicateCount(const base::string16& title, const GURL& url);

    int duplicate_count() const { return duplicate_count_; }

    NativeModelSyncState native_model_sync_state() const {
      return native_model_sync_state_;
    }
    void set_native_model_sync_state(NativeModelSyncState state) {
      native_model_sync_state_ = state;
    }

    // Bookmark roots participating in the sync.
    void AddBookmarkRoot(const bookmarks::BookmarkNode* root);
    const BookmarkList& bookmark_roots() const { return bookmark_roots_; }

    // Gets pre-association sync version for Bookmarks datatype.
    int64_t GetSyncPreAssociationVersion() const;

    void MarkForVersionUpdate(const bookmarks::BookmarkNode* node);
    const BookmarkList& bookmarks_for_version_update() const {
      return bookmarks_for_version_update_;
    }

   private:
    // DFS stack of sync nodes traversed during association.
    std::stack<int64_t> dfs_stack_;
    // Local and merge results are not owned.
    syncer::SyncMergeResult* local_merge_result_;
    syncer::SyncMergeResult* syncer_merge_result_;
    // |hashes_| contains hash codes of all native bookmarks
    // for the purpose of detecting duplicates. A small number of
    // false positives due to hash collisions is OK because this
    // data is used for reporting purposes only.
    base::hash_set<size_t> hashes_;
    // Overall number of bookmark collisions from RecordDuplicates call.
    int duplicate_count_;
    // Result of the most recent BookmarkModelAssociator::CheckModelSyncState.
    NativeModelSyncState native_model_sync_state_;
    // List of bookmark model roots participating in the sync.
    BookmarkList bookmark_roots_;
    // List of bookmark nodes for which the transaction version needs to be
    // updated.
    BookmarkList bookmarks_for_version_update_;

    DISALLOW_COPY_AND_ASSIGN(Context);
  };

  // Matches up the bookmark model and the sync model to build model
  // associations.
  syncer::SyncError BuildAssociations(Context* context);

  // Two helper functions that populate SyncMergeResult with numbers of
  // items before/after the association.
  void SetNumItemsBeforeAssociation(syncer::BaseTransaction* trans,
                                    Context* context);
  void SetNumItemsAfterAssociation(syncer::BaseTransaction* trans,
                                   Context* context);

  // Used by SetNumItemsBeforeAssociation.
  // Similar to BookmarkNode::GetTotalNodeCount but also scans the native
  // model for duplicates and records them in |context|.
  int GetTotalBookmarkCountAndRecordDuplicates(
      const bookmarks::BookmarkNode* node,
      Context* context) const;

  // Helper function that associates all tagged permanent folders and primes
  // the provided context with sync IDs of those folders.
  syncer::SyncError AssociatePermanentFolders(syncer::BaseTransaction* trans,
                                              Context* context);

  // Associate a top-level node of the bookmark model with a permanent node in
  // the sync domain.  Such permanent nodes are identified by a tag that is
  // well known to the server and the client, and is unique within a particular
  // user's share.  For example, "other_bookmarks" is the tag for the Other
  // Bookmarks folder.  The sync nodes are server-created.
  // Returns true on success, false if association failed.
  bool AssociateTaggedPermanentNode(
      syncer::BaseTransaction* trans,
      const bookmarks::BookmarkNode* permanent_node,
      const std::string& tag) WARN_UNUSED_RESULT;

  // Removes bookmark nodes whose corresponding sync nodes have been deleted
  // according to sync delete journals.
  void ApplyDeletesFromSyncJournal(syncer::BaseTransaction* trans,
                                   Context* context);

  // The main part of the association process that associatiates
  // native nodes that are children of |parent_node| with sync nodes with IDs
  // from |sync_ids|.
  syncer::SyncError BuildAssociations(
      syncer::WriteTransaction* trans,
      const bookmarks::BookmarkNode* parent_node,
      const std::vector<int64_t>& sync_ids,
      Context* context);

  // Helper method for creating a new native bookmark node.
  const bookmarks::BookmarkNode* CreateBookmarkNode(
      const bookmarks::BookmarkNode* parent_node,
      int bookmark_index,
      const syncer::BaseNode* sync_child_node,
      const GURL& url,
      Context* context,
      syncer::SyncError* error);

  // Helper method for deleting a sync node and all its children.
  // Returns the number of sync nodes deleted.
  int RemoveSyncNodeHierarchy(syncer::WriteTransaction* trans, int64_t sync_id);

  // Check whether bookmark model and sync model are synced by comparing
  // their transaction versions.
  // Returns a PERSISTENCE_ERROR if a transaction mismatch was detected where
  // the native model has a newer transaction verison.
  syncer::SyncError CheckModelSyncState(Context* context) const;

  base::ThreadChecker thread_checker_;
  bookmarks::BookmarkModel* bookmark_model_;
  sync_driver::SyncClient* sync_client_;
  syncer::UserShare* user_share_;
  sync_driver::DataTypeErrorHandler* unrecoverable_error_handler_;
  const bool expect_mobile_bookmarks_folder_;
  BookmarkIdToSyncIdMap id_map_;
  SyncIdToBookmarkNodeMap id_map_inverse_;
  // Stores sync ids for dirty associations.
  DirtyAssociationsSyncIds dirty_associations_sync_ids_;

  // Used to post PersistAssociation tasks to the current message loop and
  // guarantees no invocations can occur if |this| has been deleted. (This
  // allows this class to be non-refcounted).
  base::WeakPtrFactory<BookmarkModelAssociator> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(BookmarkModelAssociator);
};

}  // namespace browser_sync

#endif  // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_ASSOCIATOR_H_