File: partitioned_visitedlink_writer.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 (344 lines) | stat: -rw-r--r-- 14,296 bytes parent folder | download | duplicates (6)
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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
// Copyright 2024 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_VISITEDLINK_BROWSER_PARTITIONED_VISITEDLINK_WRITER_H_
#define COMPONENTS_VISITEDLINK_BROWSER_PARTITIONED_VISITEDLINK_WRITER_H_

#include <map>
#include <set>

#include "base/memory/read_only_shared_memory_region.h"
#include "components/visitedlink/common/visitedlink_common.h"
#include "components/visitedlink/core/visited_link.h"
#include "content/public/browser/browser_context.h"

namespace base {
class WritableSharedMemoryMapping;
}

namespace url {
class Origin;
}

namespace visitedlink {

class VisitedLinkDelegate;

// PartitionedVisitedLinkWriter constructs and writes to the partitioned
// :visited links hashtable. There should only be one instance of
// PartitionedVisitedLinkWriter, and it must be initialized before use.
//
// Much of this code is similar to or identical to the (unpartitioned)
// VisitedLinkWriter class. PartitionedVisitedLinkWriter does not persist to
// disk, the code has been "forked" into a separate class that relies on the
// HistoryService's VisitedLinkDatabase to persist partitioned :visited link
// browsing history across sessions. Once constructed from the
// VisitedLinkDatabase, the partitioned hashtable is stored in a shared memory
// instance.
class PartitionedVisitedLinkWriter : public VisitedLinkCommon {
 public:
  // Listens to the link coloring database events. The writer is given this
  // event as a constructor argument and dispatches events using it.
  class Listener {
   public:
    virtual ~Listener() = default;

    // Called when link coloring database has been created or replaced. The
    // argument is a memory region containing the new table.
    virtual void NewTable(base::ReadOnlySharedMemoryRegion* table_region) = 0;

    // Called when new link has been added. The argument is the fingerprint
    // (hash) of the link.
    virtual void Add(Fingerprint fingerprint) = 0;

    // Called when link coloring state has been reset. This may occur when
    // entire or parts of history were deleted. Also this may occur when
    // the table was rebuilt or loaded. The salt is stored in the database file.
    // As a result the salt will change after loading the table from the
    // database file. In this case we use |invalidate_hashes| to inform that
    // all cached visitedlink hashes need to be recalculated.
    virtual void Reset(bool invalidate_hashes) = 0;

    // This function determines the per-origin salts required for
    // any navigations that took place during the hashtable build (and as a
    // result did not send a per-origin salt in the navigation params - see
    // PartitionedVisitedLinkWriter::salts_ for more information). The
    // per-origin salts are sent via IPC to their respective VisitedLinkReader
    // instances.
    // NOTE: this is called on the main thread once the hashtable has
    // completed building on the DB thread.
    virtual void UpdateOriginSalts() = 0;
  };

  PartitionedVisitedLinkWriter(content::BrowserContext* browser_context,
                               VisitedLinkDelegate* delegate);

  // This constructor is used by unit tests.
  PartitionedVisitedLinkWriter(std::unique_ptr<Listener> listener,
                               VisitedLinkDelegate* delegate,
                               bool suppress_build,
                               int32_t default_table_size);

  ~PartitionedVisitedLinkWriter() override;

  // Must be called immediately after object creation. Nothing else will work
  // until this is called. Returns true on success, false means that this
  // object won't work.
  bool Init();

  // Adds a VisitedLink to the hashtable.
  void AddVisitedLink(const VisitedLink& link);

  // See DeleteURLs.
  class VisitedLinkIterator {
   public:
    // HasNextVisitedLink must return true when this is called. Returns the next
    // VisitedLink then advances the iterator. Note that the returned reference
    // is only valid until the next call of NextVisitedLink.
    virtual const VisitedLink& NextVisitedLink() = 0;

    // Returns true if still has VisitedLinks to be iterated.
    virtual bool HasNextVisitedLink() const = 0;

   protected:
    virtual ~VisitedLinkIterator() = default;
  };

  // Deletes the specified VisitedLinks from the hashtable.
  void DeleteVisitedLinks(VisitedLinkIterator* iterator);

  // Clears all VisitedLinks from the hashtable.
  void DeleteAllVisitedLinks();

  // Return the salt used to hash visited links from this origin. If we have not
  // visited this origin before, a new <origin, salt> pair will be added to the
  // map, and that new salt value will be retuned. Will return
  // std::optional if the table is currently being built or rebuilt.
  //
  // NOTE: THIS FUNCTION MAY ONLY BE CALLED ON THE MAIN (UI) THREAD.
  std::optional<uint64_t> GetOrAddOriginSalt(const url::Origin& origin);

#if defined(UNIT_TEST) || !defined(NDEBUG) || defined(PERF_TEST)
  // This is a debugging function that can be called to double-check internal
  // data structures. It will assert if the check fails.
  void DebugValidate();

  // Sets a task to execute when we've completed building the table from
  // history. This is ONLY used by unit tests to wait for the build to complete
  // before they continue. The pointer will be owned by this object after the
  // call.
  void set_build_complete_task(base::OnceClosure task) {
    DCHECK(build_complete_task_.is_null());
    build_complete_task_ = std::move(task);
  }

  // Returns the number non-null hashes in the table for testing verification.
  int32_t GetUsedCount() const { return used_items_; }

  // Returns the listener.
  PartitionedVisitedLinkWriter::Listener* GetListener() const {
    return listener_.get();
  }

  // Returns the hashtable stored in memory.
  const base::MappedReadOnlyRegion& GetMappedTableMemoryForTesting() {
    return mapped_table_memory_;
  }
#endif

 private:
  FRIEND_TEST_ALL_PREFIXES(PartitionedVisitedLinkTest, DeleteWithCollisions);
  FRIEND_TEST_ALL_PREFIXES(PartitionedVisitedLinkTest, HashRangeWraparound);

  // When creating an empty table, we use this many entries (see the .cc file).
  static const unsigned kDefaultTableSize;
  // Object to build the table on the history thread (see the .cc file).
  class TableBuilder;

  // General Table Handling ----------------------------------------------------

  // Creates an empty partitioned hashtable. The table is populated with the
  // partitioned shared header and filled with null hashes. The result of
  // allocation is saved into `mapped_table_memory`.
  bool CreateVisitedLinkTable(int32_t num_entries);

  // Allocates the Fingerprint structure and length. Returns true on success.
  static bool CreateVisitedLinkTableHelper(int32_t num_entries,
                                           base::MappedReadOnlyRegion* memory);

  // ResizeTableIfNecessary will check to see if the table should be resized and
  // calls ResizeTable if needed. Returns true if we decided to resize the
  // table.
  bool ResizeTableIfNecessary();

  // Resizes the table (growing or shrinking) as necessary to accommodate the
  // current count.
  void ResizeTable(int32_t new_size);

  // Computes the table load as fraction. For example, if 1/4 of the entries are
  // full, this value will be 0.25
  float ComputeTableLoad() const {
    return static_cast<float>(used_items_) / static_cast<float>(table_length_);
  }

  // Populates the partitioned hashtable based on the browser history stored
  // in the VisitedLinkDatabase. This will set table_builder_ while working, and
  // there should not already be a build occurring when called. See the
  // definition for more details on how this works.
  //
  // Returns true on success. Failure means we're not attempting to build
  // from the VisitedLinkDatabase because something failed.
  bool BuildTableFromDelegate();

  // Callback that the table builder uses when the build is complete.
  // `success` is true if the fingerprint generation succeeded, in which case
  // `fingerprints` will contain the computed fingerprints. On failure, there
  // will be no fingerprints. `salts` will contain the origin salts used to
  // generate the fingerprints. On failure, there will be no salts.
  void OnTableBuildComplete(bool success,
                            const std::vector<Fingerprint>& fingerprints,
                            std::map<url::Origin, uint64_t> salts);

  // If a build is in progress, we save `link` in `added_during_build`.
  // Otherwise, we add to the hashtable. Returns the index of the inserted
  // fingerprint or `null_hash_` on failure.
  Hash TryToAddVisitedLink(const VisitedLink& link);

  // Increases or decreases the given hash value by one, wrapping around as
  // necessary. Used for probing.
  inline Hash IncrementHash(Hash hash) {
    if (hash >= table_length_ - 1) {
      return 0;  // Wrap around.
    }
    return hash + 1;
  }
  inline Hash DecrementHash(Hash hash) {
    if (hash <= 0) {
      return table_length_ - 1;  // Wrap around.
    }
    return hash - 1;
  }

  // Called to add a fingerprint to the table. Returns the index of the inserted
  // fingerprint or null_hash_ if there was a duplicate and this item was
  // skipped.
  //
  // TODO(crbug.com/332364003): If `send_notifications` is true
  // and the item is added successfully, Listener::Add will be invoked.
  Hash AddFingerprint(Fingerprint fingerprint, bool send_notifications);

  // Deletes all fingerprints from the given vector from the current hashtable.
  // This does not update the `deleted_during_build_` list, the caller must
  // update this itself if there is a build pending.
  void DeleteFingerprintsFromCurrentTable(
      const std::set<Fingerprint>& fingerprints);

  // Removes the indicated fingerprint from the table. Returns true if the
  // fingerprint was deleted, false if it was not in the table to delete.
  bool DeleteFingerprint(Fingerprint fingerprint);

  // Returns a pointer to the start of the hash table, given the mapping
  // containing the hash table.
  static Fingerprint* GetHashTableFromMapping(
      base::WritableSharedMemoryMapping& hash_table_mapping);

  // Returns the default table size. It can be overridden in unit tests.
  uint32_t DefaultTableSize() const;

  // Returns the desired table size for storing `item_count` visited links.
  uint32_t NewTableSizeForCount(int32_t item_count) const;

  // Member variables ----------------------------------------------------------

  // TODO(crbug.com/332364003): We need to create an instance of
  // VisitedLinkEventListener to handle incoming events and define the Listener
  // class in the .h file.

  // When non-NULL, indicates we are building the hashtable from the
  // VisitedLinkDatabase.
  scoped_refptr<TableBuilder> table_builder_;

  // Contains the fingerprints of visited links that have been added or deleted
  // on the main thread since we started building the hashtable on the DB
  // thread.
  std::set<VisitedLink> added_during_build_;
  std::set<VisitedLink> deleted_during_build_;

  // Shared memory consisting of a PartitionedSharedHeader followed by the
  // table.
  base::MappedReadOnlyRegion mapped_table_memory_;

  // Number of non-empty items in the table, used to compute fullness.
  int32_t used_items_ = 0;

  // Reference to the browser context that this object belongs to
  // (it knows the path to where the data is stored).
  raw_ptr<content::BrowserContext> browser_context_ = nullptr;

  // Client owns the delegate and is responsible for it being valid through
  // the lifetime this PartitionedVisitedLinkWriter.
  raw_ptr<VisitedLinkDelegate> delegate_;

  // VisitedLinkEventListener to handle incoming events.
  std::unique_ptr<Listener> listener_;

  // Contains every per-origin salt used in creating the hashtable. Callers
  // should only access on the main (UI) thread.
  //
  // NOTE: When VisitedLinkWriter is created, `salts_` is initially empty.
  // The <origin, salt> pairs populating the map are calculated on a background
  // thread and assigned on the main thread. When this is happening,
  // `table_builder_` is non-null, and `salts_` CANNOT be added to or accessed
  // by the UI thread.
  //
  // Once initialization is complete and `table_builder_` is set to null again,
  // `salts_` can be added to and accessed by the UI thread, whether we are
  // adding new visits via the History Service or sending salt values via the
  // VisitedLinksNavigationThrottle.
  //
  // TODO(crbug.com/330548738): Currently we store all salts relevant to this
  // profile in this one map, but there can be many StoragePartitions per
  // profile. We should revisit in a future phase to take into account which
  // StoragePartition each origin is being committed to.
  std::map<url::Origin, uint64_t> salts_;

  // Testing values ----------------------------------------------------------

  // Set to fail CreateVisitedLinkTable(), to simulate shared memory allocation
  // failure. This is used for testing, will be false in production.
  static bool fail_table_creation_for_testing_;

  // Set to prevent us from building from the VisitedLinkDatabase. This is used
  // for testing and will be false in production.
  const bool suppress_build_ = false;

  // When nonzero, overrides the table size for testing.
  const int32_t table_size_override_ = 0;

  // When set, indicates the task that should be run after the next build from
  // history is complete.
  base::OnceClosure build_complete_task_;

  base::WeakPtrFactory<PartitionedVisitedLinkWriter> weak_ptr_factory_{this};
};

// NOTE: These methods are defined inline here, so we can share the compilation
// of partitioned_visitedlink_writer.cc between the browser and the unit/perf
// tests.

#if defined(UNIT_TEST) || defined(PERF_TEST) || !defined(NDEBUG)
inline void PartitionedVisitedLinkWriter::DebugValidate() {
  int32_t used_count = 0;
  for (int32_t i = 0; i < table_length_; i++) {
    if (hash_table_[i]) {
      used_count++;
    }
  }
  DCHECK_EQ(used_count, used_items_);
}
#endif

}  // namespace visitedlink

#endif  // COMPONENTS_VISITEDLINK_BROWSER_PARTITIONED_VISITEDLINK_WRITER_H_