File: bookmark_model_merger.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (254 lines) | stat: -rw-r--r-- 11,534 bytes parent folder | download | duplicates (5)
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
// Copyright 2018 The Chromium Authors
// 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_MERGER_H_
#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_MERGER_H_

#include <list>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
#include "components/sync/base/unique_position.h"
#include "components/sync/engine/commit_and_get_updates_types.h"

namespace bookmarks {
class BookmarkNode;
}  // namespace bookmarks

namespace favicon {
class FaviconService;
}  // namespace favicon

namespace sync_bookmarks {

class BookmarkModelView;
class SyncedBookmarkTracker;

// Responsible for merging local and remote bookmark models when bookmark sync
// is enabled for the first time by the user (i.e. no sync metadata exists
// locally), so we need a best-effort merge based on similarity. It is used by
// the BookmarkDataTypeProcessor().
class BookmarkModelMerger {
 public:
  // |bookmark_model|, |favicon_service| and |bookmark_tracker| must not be
  // null and must outlive this object.
  BookmarkModelMerger(syncer::UpdateResponseDataList updates,
                      BookmarkModelView* bookmark_model,
                      favicon::FaviconService* favicon_service,
                      SyncedBookmarkTracker* bookmark_tracker,
                      syncer::PreviouslySyncingGaiaIdInfoForMetrics
                          previously_syncing_gaia_id_info);

  BookmarkModelMerger(const BookmarkModelMerger&) = delete;
  BookmarkModelMerger& operator=(const BookmarkModelMerger&) = delete;

  ~BookmarkModelMerger();

  // Merges the remote bookmark model represented as the updates received from
  // the sync server and local bookmark model |bookmark_model|, and updates the
  // model and |bookmark_tracker| (all of which are injected in the constructor)
  // accordingly. On return, there will be a 1:1 mapping between bookmark nodes
  // and metadata entities in the injected tracker.
  void Merge();

  // Internal representation of a remote tree, composed of nodes. Exposed
  // publicly for metric recording.
  class RemoteTreeNode final {
   private:
    using UpdatesPerParentUuid =
        std::unordered_map<base::Uuid,
                           std::list<syncer::UpdateResponseData>,
                           base::UuidHash>;

   public:
    // Constructs a tree given |update| as root and recursively all descendants
    // by traversing |*updates_per_parent_uuid|. |update| and
    // |updates_per_parent_uuid| must not be null. All updates
    // |*updates_per_parent_uuid| must represent valid updates. Updates
    // corresponding from descendant nodes are moved away from
    // |*updates_per_parent_uuid|. |max_depth| is the max tree depth to sync
    // after which content is silently ignored.
    static RemoteTreeNode BuildTree(
        syncer::UpdateResponseData update,
        size_t max_depth,
        UpdatesPerParentUuid* updates_per_parent_uuid);

    // Test-only factory function.
    static RemoteTreeNode BuildForTesting(syncer::UpdateResponseData update,
                                          std::vector<RemoteTreeNode> children);

    ~RemoteTreeNode();

    // Allow moves, useful during construction.
    RemoteTreeNode(RemoteTreeNode&&);
    RemoteTreeNode& operator=(RemoteTreeNode&&);

    const syncer::EntityData& entity() const { return update_.entity; }
    int64_t response_version() const { return update_.response_version; }

    // Direct children nodes, sorted by ascending unique position. These are
    // guaranteed to be valid updates (e.g. IsValidBookmarkSpecifics()).
    const std::vector<RemoteTreeNode>& children() const { return children_; }

    // Recursively emplaces all UUIDs (this node and descendants) into
    // |*uuid_to_remote_node_map|, which must not be null.
    void EmplaceSelfAndDescendantsByUuid(
        std::unordered_map<base::Uuid, const RemoteTreeNode*, base::UuidHash>*
            uuid_to_remote_node_map) const;

   private:
    static bool UniquePositionLessThan(const RemoteTreeNode& lhs,
                                       const RemoteTreeNode& rhs);

    RemoteTreeNode();

    syncer::UpdateResponseData update_;
    // Redundant, parsed instance of the unique position in specifics, used
    // to sort siblings by their position information.
    syncer::UniquePosition unique_position_;
    std::vector<RemoteTreeNode> children_;
  };

  // A forest composed of multiple trees where the root of each tree represents
  // a permanent node, keyed by server-defined unique tag of the root.
  using RemoteForest = std::unordered_map<std::string, RemoteTreeNode>;

 private:
  // Represents a pair of bookmarks, one local and one remote, that have been
  // matched by UUID. They are guaranteed to have the same type and URL (if
  // applicable).
  struct GuidMatch {
    raw_ptr<const bookmarks::BookmarkNode> local_node = nullptr;
    raw_ptr<const RemoteTreeNode> remote_node = nullptr;
  };

  // Constructs the remote bookmark tree to be merged. Each entry in the
  // returned map is a permanent node, identified (keyed) by the server-defined
  // tag. All invalid updates are filtered out, including invalid bookmark
  // specifics as well as tombstones, in the unlikely event that the server
  // sends tombstones as part of the initial download.
  // |tracker_for_recording_ignored_updates| must not be null and is exclusively
  // used to record which updates where ignored because their parent couldn't be
  // determined.
  static RemoteForest BuildRemoteForest(
      syncer::UpdateResponseDataList updates,
      SyncedBookmarkTracker* tracker_for_recording_ignored_updates);

  // Recursively counts and returns the number of descendants for |node|,
  // excluding |node| itself.
  static int CountRemoteTreeNodeDescendantsForUma(
      const BookmarkModelMerger::RemoteTreeNode& node);

  // Computes bookmark pairs that should be matched by UUID. Local bookmark
  // UUIDs may be regenerated for the case where they collide with a remote UUID
  // that is not compatible (e.g. folder vs non-folder).
  static std::unordered_map<base::Uuid, GuidMatch, base::UuidHash>
  FindGuidMatchesOrReassignLocal(const RemoteForest& remote_forest,
                                 BookmarkModelView* bookmark_model);

  // Merges a local and a remote subtrees. The input nodes are two equivalent
  // local and remote nodes. This method tries to recursively match their
  // children. It updates the |bookmark_tracker_| accordingly.
  void MergeSubtree(const bookmarks::BookmarkNode* local_node,
                    const RemoteTreeNode& remote_node);

  // Makes a second pass on previously-merged subtree to detect if any of the
  // remote updates are lacking a client tag hash. If so, it migrates the entity
  // by issuing a deletion and a creation, using a new random GUID.
  void MigrateBookmarksInSubtreeWithoutClientTagHash(
      const RemoteTreeNode& remote_node);

  // Updates |local_node| to hold same UUID and semantics as its |remote_node|
  // match. The input nodes are two equivalent local and remote bookmarks that
  // are about to be merged. The output node is the potentially replaced
  // |local_node|. |local_node| must not be a BookmarkPermanentNode.
  const bookmarks::BookmarkNode* UpdateBookmarkNodeFromSpecificsIncludingUuid(
      const bookmarks::BookmarkNode* local_node,
      const RemoteTreeNode& remote_node);

  // Creates a local bookmark node for a |remote_node|. The local node is
  // created under |local_parent| at position |index|. If the remote node has
  // children, this method recursively creates them as well. It updates the
  // |bookmark_tracker_| accordingly.
  void ProcessRemoteCreation(const RemoteTreeNode& remote_node,
                             const bookmarks::BookmarkNode* local_parent,
                             size_t index);

  // Creates a server counter-part for the local node at position |index|
  // under |parent|. If the local node has children, corresponding server nodes
  // are created recursively. It updates the |bookmark_tracker_| accordingly and
  // new nodes are marked to be committed.
  void ProcessLocalCreation(const bookmarks::BookmarkNode* parent,
                            size_t index);

  // Looks for a local node under |local_parent| that matches |remote_node|,
  // starting at index |local_child_start_index|. First attempts to find a match
  // by UUID and otherwise attempts to find one by semantics. If no match is
  // found, a nullptr is returned.
  const bookmarks::BookmarkNode* FindMatchingLocalNode(
      const RemoteTreeNode& remote_node,
      const bookmarks::BookmarkNode* local_parent,
      size_t local_child_start_index) const;

  // If |local_node| has a remote counterpart of the same UUID, returns the
  // corresponding remote node, otherwise returns a nullptr. |local_node| must
  // not be null.
  const RemoteTreeNode* FindMatchingRemoteNodeByUuid(
      const bookmarks::BookmarkNode* local_node) const;

  // If |remote_node| has a local counterpart of the same UUID, returns the
  // corresponding local node, otherwise returns a nullptr.
  const bookmarks::BookmarkNode* FindMatchingLocalNodeByUuid(
      const RemoteTreeNode& remote_node) const;

  // Tries to find a child local node under |local_parent| that matches
  // |remote_node| semantically and returns the index of that node, as long as
  // this local child cannot be matched by UUID to a different node. Matching is
  // decided using NodeSemanticsMatch(). It searches in the children list
  // starting from position |search_starting_child_index|. In case of no match
  // is found, it returns |kInvalidIndex|.
  size_t FindMatchingChildBySemanticsStartingAt(
      const RemoteTreeNode& remote_node,
      const bookmarks::BookmarkNode* local_parent,
      size_t starting_child_index) const;

  // Used to generate a unique position for the current locally created
  // bookmark.
  syncer::UniquePosition GenerateUniquePositionForLocalCreation(
      const bookmarks::BookmarkNode* parent,
      size_t index,
      const syncer::UniquePosition::Suffix& suffix) const;

  void ReportTimeMetrics();

  // The base time used to calculate elapsed time at different stages during the
  // initial merge. Should be the first member to initialize before any other
  // long operations like BuildRemoteForest().
  const base::TimeTicks started_ = base::TimeTicks::Now();

  const raw_ptr<BookmarkModelView> bookmark_model_;
  const raw_ptr<favicon::FaviconService> favicon_service_;
  const raw_ptr<SyncedBookmarkTracker> bookmark_tracker_;
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS)
  const syncer::PreviouslySyncingGaiaIdInfoForMetrics
      previously_syncing_gaia_id_info_;
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
        // !BUILDFLAG(IS_CHROMEOS)
  const size_t remote_updates_size_;
  // Preprocessed remote nodes in the form a forest where each tree's root is a
  // permanent node. Computed upon construction via BuildRemoteForest().
  const RemoteForest remote_forest_;
  std::unordered_map<base::Uuid, GuidMatch, base::UuidHash> uuid_to_match_map_;
};

}  // namespace sync_bookmarks

#endif  // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_MERGER_H_