1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/extras/sqlite/sqlite_persistent_shared_dictionary_store.h"
6
7 #include "base/containers/span.h"
8 #include "base/debug/dump_without_crashing.h"
9 #include "base/files/file_path.h"
10 #include "base/metrics/histogram_functions.h"
11 #include "base/pickle.h"
12 #include "base/strings/strcat.h"
13 #include "base/task/sequenced_task_runner.h"
14 #include "base/types/expected_macros.h"
15 #include "net/base/network_isolation_key.h"
16 #include "net/extras/shared_dictionary/shared_dictionary_isolation_key.h"
17 #include "net/extras/sqlite/sqlite_persistent_store_backend_base.h"
18 #include "sql/database.h"
19 #include "sql/statement.h"
20 #include "sql/transaction.h"
21
22 namespace net {
23
24 namespace {
25
26 constexpr char kHistogramTag[] = "SharedDictionary";
27
28 constexpr char kHistogramPrefix[] = "Net.SharedDictionaryStore.";
29
30 constexpr char kTableName[] = "dictionaries";
31
32 // The key for storing the total dictionary size in MetaTable. It is utilized
33 // when determining whether cache eviction needs to be performed. We store it as
34 // metadata because calculating the total size is an expensive operation.
35 constexpr char kTotalDictSizeKey[] = "total_dict_size";
36
37 const int kCurrentVersionNumber = 2;
38 const int kCompatibleVersionNumber = 2;
39
CreateV2Schema(sql::Database * db,sql::MetaTable * meta_table)40 bool CreateV2Schema(sql::Database* db, sql::MetaTable* meta_table) {
41 CHECK(!db->DoesTableExist(kTableName));
42
43 static constexpr char kCreateTableQuery[] =
44 // clang-format off
45 "CREATE TABLE dictionaries("
46 "primary_key INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
47 "frame_origin TEXT NOT NULL,"
48 "top_frame_site TEXT NOT NULL,"
49 "host TEXT NOT NULL,"
50 "match TEXT NOT NULL,"
51 "match_dest TEXT NOT NULL,"
52 "id TEXT NOT NULL,"
53 "url TEXT NOT NULL,"
54 "res_time INTEGER NOT NULL,"
55 "exp_time INTEGER NOT NULL,"
56 "last_used_time INTEGER NOT NULL,"
57 "size INTEGER NOT NULL,"
58 "sha256 BLOB NOT NULL,"
59 "token_high INTEGER NOT NULL,"
60 "token_low INTEGER NOT NULL)";
61 // clang-format on
62
63 static constexpr char kCreateUniqueIndexQuery[] =
64 // clang-format off
65 "CREATE UNIQUE INDEX unique_index ON dictionaries("
66 "frame_origin,"
67 "top_frame_site,"
68 "host,"
69 "match,"
70 "match_dest)";
71 // clang-format on
72
73 // This index is used for the size and count limitation per top_frame_site.
74 static constexpr char kCreateTopFrameSiteIndexQuery[] =
75 // clang-format off
76 "CREATE INDEX top_frame_site_index ON dictionaries("
77 "top_frame_site)";
78 // clang-format on
79
80 // This index is used for GetDictionaries().
81 static constexpr char kCreateIsolationIndexQuery[] =
82 // clang-format off
83 "CREATE INDEX isolation_index ON dictionaries("
84 "frame_origin,"
85 "top_frame_site)";
86 // clang-format on
87
88 // This index will be used when implementing garbage collection logic of
89 // SharedDictionaryDiskCache.
90 static constexpr char kCreateTokenIndexQuery[] =
91 // clang-format off
92 "CREATE INDEX token_index ON dictionaries("
93 "token_high, token_low)";
94 // clang-format on
95
96 // This index will be used when implementing clearing expired dictionary
97 // logic.
98 static constexpr char kCreateExpirationTimeIndexQuery[] =
99 // clang-format off
100 "CREATE INDEX exp_time_index ON dictionaries("
101 "exp_time)";
102 // clang-format on
103
104 // This index will be used when implementing clearing dictionary logic which
105 // will be called from BrowsingDataRemover.
106 static constexpr char kCreateLastUsedTimeIndexQuery[] =
107 // clang-format off
108 "CREATE INDEX last_used_time_index ON dictionaries("
109 "last_used_time)";
110 // clang-format on
111
112 sql::Transaction transaction(db);
113 if (!transaction.Begin() || !db->Execute(kCreateTableQuery) ||
114 !db->Execute(kCreateUniqueIndexQuery) ||
115 !db->Execute(kCreateTopFrameSiteIndexQuery) ||
116 !db->Execute(kCreateIsolationIndexQuery) ||
117 !db->Execute(kCreateTokenIndexQuery) ||
118 !db->Execute(kCreateExpirationTimeIndexQuery) ||
119 !db->Execute(kCreateLastUsedTimeIndexQuery) ||
120 !meta_table->SetValue(kTotalDictSizeKey, 0) || !transaction.Commit()) {
121 return false;
122 }
123 return true;
124 }
125
ToSHA256HashValue(base::span<const uint8_t> sha256_bytes)126 std::optional<SHA256HashValue> ToSHA256HashValue(
127 base::span<const uint8_t> sha256_bytes) {
128 SHA256HashValue sha256_hash;
129 if (sha256_bytes.size() != sizeof(sha256_hash.data)) {
130 return std::nullopt;
131 }
132 memcpy(sha256_hash.data, sha256_bytes.data(), sha256_bytes.size());
133 return sha256_hash;
134 }
135
ToUnguessableToken(int64_t token_high,int64_t token_low)136 std::optional<base::UnguessableToken> ToUnguessableToken(int64_t token_high,
137 int64_t token_low) {
138 // There is no `sql::Statement::ColumnUint64()` method. So we cast to
139 // uint64_t.
140 return base::UnguessableToken::Deserialize(static_cast<uint64_t>(token_high),
141 static_cast<uint64_t>(token_low));
142 }
143
144 template <typename ResultType>
WrapCallbackWithWeakPtrCheck(base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,base::OnceCallback<void (ResultType)> callback)145 base::OnceCallback<void(ResultType)> WrapCallbackWithWeakPtrCheck(
146 base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
147 base::OnceCallback<void(ResultType)> callback) {
148 return base::BindOnce(
149 [](base::WeakPtr<SQLitePersistentSharedDictionaryStore> weak_ptr,
150 base::OnceCallback<void(ResultType)> callback, ResultType result) {
151 if (!weak_ptr) {
152 return;
153 }
154 std::move(callback).Run(std::move(result));
155 },
156 std::move(weak_ptr), std::move(callback));
157 }
158
RecordErrorHistogram(const char * method_name,SQLitePersistentSharedDictionaryStore::Error error)159 void RecordErrorHistogram(const char* method_name,
160 SQLitePersistentSharedDictionaryStore::Error error) {
161 base::UmaHistogramEnumeration(
162 base::StrCat({kHistogramPrefix, method_name, ".Error"}), error);
163 }
164
165 template <typename ResultType>
RecordErrorHistogram(const char * method_name,base::expected<ResultType,SQLitePersistentSharedDictionaryStore::Error> result)166 void RecordErrorHistogram(
167 const char* method_name,
168 base::expected<ResultType, SQLitePersistentSharedDictionaryStore::Error>
169 result) {
170 RecordErrorHistogram(method_name,
171 result.has_value()
172 ? SQLitePersistentSharedDictionaryStore::Error::kOk
173 : result.error());
174 }
175
176 } // namespace
177
178 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult::
RegisterDictionaryResult(int64_t primary_key_in_database,std::optional<base::UnguessableToken> replaced_disk_cache_key_token,std::set<base::UnguessableToken> evicted_disk_cache_key_tokens,uint64_t total_dictionary_size,uint64_t total_dictionary_count)179 RegisterDictionaryResult(
180 int64_t primary_key_in_database,
181 std::optional<base::UnguessableToken> replaced_disk_cache_key_token,
182 std::set<base::UnguessableToken> evicted_disk_cache_key_tokens,
183 uint64_t total_dictionary_size,
184 uint64_t total_dictionary_count)
185 : primary_key_in_database_(primary_key_in_database),
186 replaced_disk_cache_key_token_(std::move(replaced_disk_cache_key_token)),
187 evicted_disk_cache_key_tokens_(std::move(evicted_disk_cache_key_tokens)),
188 total_dictionary_size_(total_dictionary_size),
189 total_dictionary_count_(total_dictionary_count) {}
190
191 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult::
192 ~RegisterDictionaryResult() = default;
193
194 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult::
195 RegisterDictionaryResult(const RegisterDictionaryResult& other) = default;
196
197 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult::
198 RegisterDictionaryResult(RegisterDictionaryResult&& other) = default;
199
200 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult&
201 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult::operator=(
202 const RegisterDictionaryResult& other) = default;
203
204 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult&
205 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResult::operator=(
206 RegisterDictionaryResult&& other) = default;
207
208 class SQLitePersistentSharedDictionaryStore::Backend
209 : public SQLitePersistentStoreBackendBase {
210 public:
Backend(const base::FilePath & path,const scoped_refptr<base::SequencedTaskRunner> & client_task_runner,const scoped_refptr<base::SequencedTaskRunner> & background_task_runner)211 Backend(
212 const base::FilePath& path,
213 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
214 const scoped_refptr<base::SequencedTaskRunner>& background_task_runner)
215 : SQLitePersistentStoreBackendBase(path,
216 kHistogramTag,
217 kCurrentVersionNumber,
218 kCompatibleVersionNumber,
219 background_task_runner,
220 client_task_runner,
221 /*enable_exclusive_access=*/false) {}
222
223 Backend(const Backend&) = delete;
224 Backend& operator=(const Backend&) = delete;
225
226 #define DEFINE_CROSS_SEQUENCE_CALL_METHOD(Name) \
227 template <typename ResultType, typename... Args> \
228 void Name(base::OnceCallback<void(ResultType)> callback, Args&&... args) { \
229 CHECK(client_task_runner()->RunsTasksInCurrentSequence()); \
230 PostBackgroundTask( \
231 FROM_HERE, \
232 base::BindOnce( \
233 [](scoped_refptr<Backend> backend, \
234 base::OnceCallback<void(ResultType)> callback, \
235 Args&&... args) { \
236 auto result = backend->Name##Impl(std::forward<Args>(args)...); \
237 RecordErrorHistogram(#Name, result); \
238 backend->PostClientTask( \
239 FROM_HERE, \
240 base::BindOnce(std::move(callback), std::move(result))); \
241 }, \
242 scoped_refptr<Backend>(this), std::move(callback), \
243 std::forward<Args>(args)...)); \
244 }
245
246 // The following methods call *Impl() method in the background task runner,
247 // and call the passed `callback` with the result in the client task runner.
248 DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetTotalDictionarySize)
249 DEFINE_CROSS_SEQUENCE_CALL_METHOD(RegisterDictionary)
250 DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetDictionaries)
251 DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetAllDictionaries)
252 DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetUsageInfo)
253 DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetOriginsBetween)
254 DEFINE_CROSS_SEQUENCE_CALL_METHOD(ClearAllDictionaries)
255 DEFINE_CROSS_SEQUENCE_CALL_METHOD(ClearDictionaries)
256 DEFINE_CROSS_SEQUENCE_CALL_METHOD(ClearDictionariesForIsolationKey)
257 DEFINE_CROSS_SEQUENCE_CALL_METHOD(DeleteExpiredDictionaries)
258 DEFINE_CROSS_SEQUENCE_CALL_METHOD(ProcessEviction)
259 DEFINE_CROSS_SEQUENCE_CALL_METHOD(GetAllDiskCacheKeyTokens)
260 DEFINE_CROSS_SEQUENCE_CALL_METHOD(DeleteDictionariesByDiskCacheKeyTokens)
261 #undef DEFINE_CROSS_SEQUENCE_CALL_METHOD
262
263 void UpdateDictionaryLastUsedTime(int64_t primary_key_in_database,
264 base::Time last_used_time);
265
266 private:
267 ~Backend() override = default;
268
269 // Gets the total dictionary size in MetaTable.
270 SizeOrError GetTotalDictionarySizeImpl();
271
272 RegisterDictionaryResultOrError RegisterDictionaryImpl(
273 const SharedDictionaryIsolationKey& isolation_key,
274 const SharedDictionaryInfo& dictionary_info,
275 uint64_t max_size_per_site,
276 uint64_t max_count_per_site);
277 DictionaryListOrError GetDictionariesImpl(
278 const SharedDictionaryIsolationKey& isolation_key);
279 DictionaryMapOrError GetAllDictionariesImpl();
280 UsageInfoOrError GetUsageInfoImpl();
281 OriginListOrError GetOriginsBetweenImpl(const base::Time start_time,
282 const base::Time end_time);
283 UnguessableTokenSetOrError ClearAllDictionariesImpl();
284 UnguessableTokenSetOrError ClearDictionariesImpl(
285 base::Time start_time,
286 base::Time end_time,
287 base::RepeatingCallback<bool(const GURL&)> url_matcher);
288 UnguessableTokenSetOrError ClearDictionariesForIsolationKeyImpl(
289 const SharedDictionaryIsolationKey& isolation_key);
290 UnguessableTokenSetOrError DeleteExpiredDictionariesImpl(base::Time now);
291 UnguessableTokenSetOrError ProcessEvictionImpl(uint64_t cache_max_size,
292 uint64_t size_low_watermark,
293 uint64_t cache_max_count,
294 uint64_t count_low_watermark);
295 UnguessableTokenSetOrError GetAllDiskCacheKeyTokensImpl();
296 Error DeleteDictionariesByDiskCacheKeyTokensImpl(
297 const std::set<base::UnguessableToken>& disk_cache_key_tokens);
298
299 // If a matching dictionary exists, populates 'size_out' and
300 // 'disk_cache_key_out' with the dictionary's respective values and returns
301 // true. Otherwise returns false.
302 bool GetExistingDictionarySizeAndDiskCacheKeyToken(
303 const SharedDictionaryIsolationKey& isolation_key,
304 const url::SchemeHostPort& host,
305 const std::string& match,
306 const std::string& match_dest,
307 int64_t* size_out,
308 std::optional<base::UnguessableToken>* disk_cache_key_out);
309
310 // Updates the total dictionary size in MetaTable by `size_delta` and returns
311 // the updated total dictionary size.
312 Error UpdateTotalDictionarySizeInMetaTable(
313 int64_t size_delta,
314 uint64_t* total_dictionary_size_out);
315
316 // Gets the total dictionary count.
317 SizeOrError GetTotalDictionaryCount();
318
319 // SQLitePersistentStoreBackendBase implementation
320 bool CreateDatabaseSchema() override;
321 std::optional<int> DoMigrateDatabaseSchema() override;
322 void DoCommit() override;
323
324 // Commits the last used time update.
325 Error CommitDictionaryLastUsedTimeUpdate(int64_t primary_key_in_database,
326 base::Time last_used_time);
327
328 // Selects dictionaries which `res_time` is between `start_time` and
329 // `end_time`. And fills their primary keys and tokens and total size.
330 Error SelectMatchingDictionaries(
331 base::Time start_time,
332 base::Time end_time,
333 std::vector<int64_t>* primary_keys_out,
334 std::vector<base::UnguessableToken>* tokens_out,
335 int64_t* total_size_out);
336 // Selects dictionaries which `res_time` is between `start_time` and
337 // `end_time`, and which `frame_origin` or `top_frame_site` or `host` matches
338 // with `url_matcher`. And fills their primary keys and tokens and total size.
339 Error SelectMatchingDictionariesWithUrlMatcher(
340 base::Time start_time,
341 base::Time end_time,
342 base::RepeatingCallback<bool(const GURL&)> url_matcher,
343 std::vector<int64_t>* primary_keys_out,
344 std::vector<base::UnguessableToken>* tokens_out,
345 int64_t* total_size_out);
346 // Selects dictionaries in order of `last_used_time` if the total size of all
347 // dictionaries exceeds `cache_max_size` or the total dictionary count exceeds
348 // `cache_max_count` until the total size reaches `size_low_watermark` and the
349 // total count reaches `count_low_watermark`, and fills their primary keys and
350 // tokens and total size. If `cache_max_size` is zero, the size limitation is
351 // ignored.
352 Error SelectEvictionCandidates(
353 uint64_t cache_max_size,
354 uint64_t size_low_watermark,
355 uint64_t cache_max_count,
356 uint64_t count_low_watermark,
357 std::vector<int64_t>* primary_keys_out,
358 std::vector<base::UnguessableToken>* tokens_out,
359 int64_t* total_size_after_eviction_out);
360 // Deletes a dictionary with `primary_key`.
361 Error DeleteDictionaryByPrimaryKey(int64_t primary_key);
362 // Deletes a dictionary with `disk_cache_key_token` and returns the deleted
363 // dictionarie's size.
364 SizeOrError DeleteDictionaryByDiskCacheToken(
365 const base::UnguessableToken& disk_cache_key_token);
366
367 Error MaybeEvictDictionariesForPerSiteLimit(
368 const SchemefulSite& top_frame_site,
369 uint64_t max_size_per_site,
370 uint64_t max_count_per_site,
371 std::vector<base::UnguessableToken>* evicted_disk_cache_key_tokens,
372 uint64_t* total_dictionary_size_out);
373 SizeOrError GetDictionaryCountPerSite(const SchemefulSite& top_frame_site);
374 SizeOrError GetDictionarySizePerSite(const SchemefulSite& top_frame_site);
375 Error SelectCandidatesForPerSiteEviction(
376 const SchemefulSite& top_frame_site,
377 uint64_t max_size_per_site,
378 uint64_t max_count_per_site,
379 std::vector<int64_t>* primary_keys_out,
380 std::vector<base::UnguessableToken>* tokens_out,
381 int64_t* total_candidate_dictionary_size_out);
382
383 // Total number of pending last used time update operations (may not match the
384 // size of `pending_last_used_time_updates_`, due to operation coalescing).
385 size_t num_pending_ GUARDED_BY(lock_) = 0;
386 std::map<int64_t, base::Time> pending_last_used_time_updates_
387 GUARDED_BY(lock_);
388 // Protects `num_pending_`, and `pending_last_used_time_updates_`.
389 mutable base::Lock lock_;
390 };
391
CreateDatabaseSchema()392 bool SQLitePersistentSharedDictionaryStore::Backend::CreateDatabaseSchema() {
393 if (!db()->DoesTableExist(kTableName) &&
394 !CreateV2Schema(db(), meta_table())) {
395 return false;
396 }
397 return true;
398 }
399
400 std::optional<int>
DoMigrateDatabaseSchema()401 SQLitePersistentSharedDictionaryStore::Backend::DoMigrateDatabaseSchema() {
402 int cur_version = meta_table()->GetVersionNumber();
403 if (cur_version == 1) {
404 sql::Transaction transaction(db());
405 if (!transaction.Begin() ||
406 !db()->Execute("DROP TABLE IF EXISTS dictionaries") ||
407 !meta_table()->DeleteKey(kTotalDictSizeKey)) {
408 return std::nullopt;
409 }
410 // The version 1 is used during the Origin Trial period (M119-M122).
411 // We don't need to migrate the data from version 1.
412 ++cur_version;
413 if (!meta_table()->SetVersionNumber(cur_version) ||
414 !meta_table()->SetCompatibleVersionNumber(
415 std::min(cur_version, kCompatibleVersionNumber)) ||
416 !transaction.Commit()) {
417 return std::nullopt;
418 }
419 }
420
421 // Future database upgrade statements go here.
422
423 return std::make_optional(cur_version);
424 }
425
DoCommit()426 void SQLitePersistentSharedDictionaryStore::Backend::DoCommit() {
427 std::map<int64_t, base::Time> pending_last_used_time_updates;
428 {
429 base::AutoLock locked(lock_);
430 pending_last_used_time_updates_.swap(pending_last_used_time_updates);
431 num_pending_ = 0;
432 }
433 if (!db() || pending_last_used_time_updates.empty()) {
434 return;
435 }
436
437 sql::Transaction transaction(db());
438 if (!transaction.Begin()) {
439 return;
440 }
441 for (const auto& it : pending_last_used_time_updates) {
442 if (CommitDictionaryLastUsedTimeUpdate(it.first, it.second) != Error::kOk) {
443 return;
444 }
445 }
446 transaction.Commit();
447 }
448
449 SQLitePersistentSharedDictionaryStore::Error
450 SQLitePersistentSharedDictionaryStore::Backend::
CommitDictionaryLastUsedTimeUpdate(int64_t primary_key_in_database,base::Time last_used_time)451 CommitDictionaryLastUsedTimeUpdate(int64_t primary_key_in_database,
452 base::Time last_used_time) {
453 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
454 if (!InitializeDatabase()) {
455 return Error::kFailedToInitializeDatabase;
456 }
457 static constexpr char kQuery[] =
458 "UPDATE dictionaries SET last_used_time=? WHERE primary_key=?";
459
460 if (!db()->IsSQLValid(kQuery)) {
461 return Error::kInvalidSql;
462 }
463 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
464 statement.BindTime(0, last_used_time);
465 statement.BindInt64(1, primary_key_in_database);
466 if (!statement.Run()) {
467 return Error::kFailedToExecuteSql;
468 }
469 return Error::kOk;
470 }
471
472 base::expected<uint64_t, SQLitePersistentSharedDictionaryStore::Error>
GetTotalDictionarySizeImpl()473 SQLitePersistentSharedDictionaryStore::Backend::GetTotalDictionarySizeImpl() {
474 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
475 if (!InitializeDatabase()) {
476 return base::unexpected(Error::kFailedToInitializeDatabase);
477 }
478
479 int64_t unsigned_total_dictionary_size = 0;
480 if (!meta_table()->GetValue(kTotalDictSizeKey,
481 &unsigned_total_dictionary_size)) {
482 return base::unexpected(Error::kFailedToGetTotalDictSize);
483 }
484
485 // There is no `sql::Statement::ColumnUint64()` method. So we cast to
486 // uint64_t.
487 return base::ok(static_cast<uint64_t>(unsigned_total_dictionary_size));
488 }
489
490 SQLitePersistentSharedDictionaryStore::RegisterDictionaryResultOrError
RegisterDictionaryImpl(const SharedDictionaryIsolationKey & isolation_key,const SharedDictionaryInfo & dictionary_info,uint64_t max_size_per_site,uint64_t max_count_per_site)491 SQLitePersistentSharedDictionaryStore::Backend::RegisterDictionaryImpl(
492 const SharedDictionaryIsolationKey& isolation_key,
493 const SharedDictionaryInfo& dictionary_info,
494 uint64_t max_size_per_site,
495 uint64_t max_count_per_site) {
496 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
497 CHECK_NE(0u, max_count_per_site);
498 if (max_size_per_site != 0 && dictionary_info.size() > max_size_per_site) {
499 return base::unexpected(Error::kTooBigDictionary);
500 }
501
502 if (!InitializeDatabase()) {
503 return base::unexpected(Error::kFailedToInitializeDatabase);
504 }
505
506 // Commit `pending_last_used_time_updates_`.
507 DoCommit();
508
509 sql::Transaction transaction(db());
510 if (!transaction.Begin()) {
511 return base::unexpected(Error::kFailedToBeginTransaction);
512 }
513
514 int64_t size_of_removed_dict = 0;
515 std::optional<base::UnguessableToken> replaced_disk_cache_key_token;
516 int64_t size_delta = dictionary_info.size();
517 if (GetExistingDictionarySizeAndDiskCacheKeyToken(
518 isolation_key, url::SchemeHostPort(dictionary_info.url()),
519 dictionary_info.match(), dictionary_info.match_dest_string(),
520 &size_of_removed_dict, &replaced_disk_cache_key_token)) {
521 size_delta -= size_of_removed_dict;
522 }
523
524 static constexpr char kQuery[] =
525 // clang-format off
526 "INSERT OR REPLACE INTO dictionaries("
527 "frame_origin,"
528 "top_frame_site,"
529 "host,"
530 "match,"
531 "match_dest,"
532 "id,"
533 "url,"
534 "res_time,"
535 "exp_time,"
536 "last_used_time,"
537 "size,"
538 "sha256,"
539 "token_high,"
540 "token_low) "
541 "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
542 // clang-format on
543
544 if (!db()->IsSQLValid(kQuery)) {
545 return base::unexpected(Error::kInvalidSql);
546 }
547
548 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
549 statement.BindString(0, isolation_key.frame_origin().Serialize());
550 statement.BindString(1, isolation_key.top_frame_site().Serialize());
551 statement.BindString(2,
552 url::SchemeHostPort(dictionary_info.url()).Serialize());
553 statement.BindString(3, dictionary_info.match());
554 statement.BindString(4, dictionary_info.match_dest_string());
555 statement.BindString(5, dictionary_info.id());
556 statement.BindString(6, dictionary_info.url().spec());
557 statement.BindTime(7, dictionary_info.response_time());
558 statement.BindTime(8, dictionary_info.GetExpirationTime());
559 statement.BindTime(9, dictionary_info.last_used_time());
560 statement.BindInt64(10, dictionary_info.size());
561 statement.BindBlob(11, base::make_span(dictionary_info.hash().data));
562 // There is no `sql::Statement::BindUint64()` method. So we cast to int64_t.
563 int64_t token_high = static_cast<int64_t>(
564 dictionary_info.disk_cache_key_token().GetHighForSerialization());
565 int64_t token_low = static_cast<int64_t>(
566 dictionary_info.disk_cache_key_token().GetLowForSerialization());
567 statement.BindInt64(12, token_high);
568 statement.BindInt64(13, token_low);
569
570 if (!statement.Run()) {
571 return base::unexpected(Error::kFailedToExecuteSql);
572 }
573 int64_t primary_key = db()->GetLastInsertRowId();
574
575 uint64_t total_dictionary_size = 0;
576 Error error =
577 UpdateTotalDictionarySizeInMetaTable(size_delta, &total_dictionary_size);
578 if (error != Error::kOk) {
579 return base::unexpected(error);
580 }
581
582 std::vector<base::UnguessableToken> evicted_disk_cache_key_tokens;
583 error = MaybeEvictDictionariesForPerSiteLimit(
584 isolation_key.top_frame_site(), max_size_per_site, max_count_per_site,
585 &evicted_disk_cache_key_tokens, &total_dictionary_size);
586 if (error != Error::kOk) {
587 return base::unexpected(error);
588 }
589
590 ASSIGN_OR_RETURN(uint64_t total_dictionary_count, GetTotalDictionaryCount());
591
592 if (!transaction.Commit()) {
593 return base::unexpected(Error::kFailedToCommitTransaction);
594 }
595 return base::ok(RegisterDictionaryResult{
596 primary_key, replaced_disk_cache_key_token,
597 std::set<base::UnguessableToken>(evicted_disk_cache_key_tokens.begin(),
598 evicted_disk_cache_key_tokens.end()),
599 total_dictionary_size, total_dictionary_count});
600 }
601
602 SQLitePersistentSharedDictionaryStore::Error
603 SQLitePersistentSharedDictionaryStore::Backend::
MaybeEvictDictionariesForPerSiteLimit(const SchemefulSite & top_frame_site,uint64_t max_size_per_site,uint64_t max_count_per_site,std::vector<base::UnguessableToken> * evicted_disk_cache_key_tokens,uint64_t * total_dictionary_size_out)604 MaybeEvictDictionariesForPerSiteLimit(
605 const SchemefulSite& top_frame_site,
606 uint64_t max_size_per_site,
607 uint64_t max_count_per_site,
608 std::vector<base::UnguessableToken>* evicted_disk_cache_key_tokens,
609 uint64_t* total_dictionary_size_out) {
610 std::vector<int64_t> primary_keys;
611 int64_t total_candidate_dictionary_size = 0;
612 Error error = SelectCandidatesForPerSiteEviction(
613 top_frame_site, max_size_per_site, max_count_per_site, &primary_keys,
614 evicted_disk_cache_key_tokens, &total_candidate_dictionary_size);
615 if (error != Error::kOk) {
616 return error;
617 }
618 CHECK_EQ(primary_keys.size(), evicted_disk_cache_key_tokens->size());
619 if (primary_keys.empty()) {
620 return Error::kOk;
621 }
622 for (int64_t primary_key : primary_keys) {
623 error = DeleteDictionaryByPrimaryKey(primary_key);
624 if (error != Error::kOk) {
625 return error;
626 }
627 }
628 error = UpdateTotalDictionarySizeInMetaTable(-total_candidate_dictionary_size,
629 total_dictionary_size_out);
630 if (error != Error::kOk) {
631 return error;
632 }
633 return Error::kOk;
634 }
635
636 SQLitePersistentSharedDictionaryStore::Error
637 SQLitePersistentSharedDictionaryStore::Backend::
SelectCandidatesForPerSiteEviction(const SchemefulSite & top_frame_site,uint64_t max_size_per_site,uint64_t max_count_per_site,std::vector<int64_t> * primary_keys_out,std::vector<base::UnguessableToken> * tokens_out,int64_t * total_size_of_candidates_out)638 SelectCandidatesForPerSiteEviction(
639 const SchemefulSite& top_frame_site,
640 uint64_t max_size_per_site,
641 uint64_t max_count_per_site,
642 std::vector<int64_t>* primary_keys_out,
643 std::vector<base::UnguessableToken>* tokens_out,
644 int64_t* total_size_of_candidates_out) {
645 CHECK(primary_keys_out->empty());
646 CHECK(tokens_out->empty());
647 CHECK_EQ(0, *total_size_of_candidates_out);
648
649 ASSIGN_OR_RETURN(uint64_t size_per_site,
650 GetDictionarySizePerSite(top_frame_site));
651 ASSIGN_OR_RETURN(uint64_t count_per_site,
652 GetDictionaryCountPerSite(top_frame_site));
653
654 base::UmaHistogramMemoryKB(
655 base::StrCat({kHistogramPrefix, "DictionarySizeKBPerSiteWhenAdded"}),
656 size_per_site);
657 base::UmaHistogramCounts1000(
658 base::StrCat({kHistogramPrefix, "DictionaryCountPerSiteWhenAdded"}),
659 count_per_site);
660
661 if ((max_size_per_site == 0 || size_per_site <= max_size_per_site) &&
662 count_per_site <= max_count_per_site) {
663 return Error::kOk;
664 }
665
666 uint64_t to_be_removed_count = 0;
667 if (count_per_site > max_count_per_site) {
668 to_be_removed_count = count_per_site - max_count_per_site;
669 }
670
671 int64_t to_be_removed_size = 0;
672 if (max_size_per_site != 0 && size_per_site > max_size_per_site) {
673 to_be_removed_size = size_per_site - max_size_per_site;
674 }
675 static constexpr char kQuery[] =
676 // clang-format off
677 "SELECT "
678 "primary_key,"
679 "size,"
680 "token_high,"
681 "token_low FROM dictionaries "
682 "WHERE top_frame_site=? "
683 "ORDER BY last_used_time";
684 // clang-format on
685
686 if (!db()->IsSQLValid(kQuery)) {
687 return Error::kInvalidSql;
688 }
689 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
690 statement.BindString(0, top_frame_site.Serialize());
691
692 base::CheckedNumeric<int64_t> checked_total_size_of_candidates;
693 while (statement.Step()) {
694 const int64_t primary_key_in_database = statement.ColumnInt64(0);
695 const size_t size = statement.ColumnInt64(1);
696 const int64_t token_high = statement.ColumnInt64(2);
697 const int64_t token_low = statement.ColumnInt64(3);
698
699 std::optional<base::UnguessableToken> disk_cache_key_token =
700 ToUnguessableToken(token_high, token_low);
701 if (!disk_cache_key_token) {
702 LOG(WARNING) << "Invalid token";
703 continue;
704 }
705 checked_total_size_of_candidates += size;
706
707 if (!checked_total_size_of_candidates.IsValid()) {
708 base::debug::DumpWithoutCrashing();
709 return Error::kInvalidTotalDictSize;
710 }
711
712 *total_size_of_candidates_out =
713 checked_total_size_of_candidates.ValueOrDie();
714 primary_keys_out->emplace_back(primary_key_in_database);
715 tokens_out->emplace_back(*disk_cache_key_token);
716
717 if (*total_size_of_candidates_out >= to_be_removed_size &&
718 tokens_out->size() >= to_be_removed_count) {
719 break;
720 }
721 }
722
723 return Error::kOk;
724 }
725
726 base::expected<uint64_t, SQLitePersistentSharedDictionaryStore::Error>
GetDictionaryCountPerSite(const SchemefulSite & top_frame_site)727 SQLitePersistentSharedDictionaryStore::Backend::GetDictionaryCountPerSite(
728 const SchemefulSite& top_frame_site) {
729 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
730 static constexpr char kQuery[] =
731 // clang-format off
732 "SELECT "
733 "COUNT(primary_key) FROM dictionaries "
734 "WHERE top_frame_site=?";
735 // clang-format on
736
737 if (!db()->IsSQLValid(kQuery)) {
738 return base::unexpected(Error::kInvalidSql);
739 }
740 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
741 statement.BindString(0, top_frame_site.Serialize());
742 uint64_t count_per_site = 0;
743 if (statement.Step()) {
744 count_per_site = statement.ColumnInt64(0);
745 }
746 return base::ok(count_per_site);
747 }
748
749 base::expected<uint64_t, SQLitePersistentSharedDictionaryStore::Error>
GetDictionarySizePerSite(const SchemefulSite & top_frame_site)750 SQLitePersistentSharedDictionaryStore::Backend::GetDictionarySizePerSite(
751 const SchemefulSite& top_frame_site) {
752 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
753 static constexpr char kQuery[] =
754 // clang-format off
755 "SELECT "
756 "SUM(size) FROM dictionaries "
757 "WHERE top_frame_site=?";
758 // clang-format on
759
760 if (!db()->IsSQLValid(kQuery)) {
761 return base::unexpected(Error::kInvalidSql);
762 }
763 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
764 statement.BindString(0, top_frame_site.Serialize());
765 uint64_t size_per_site = 0;
766 if (statement.Step()) {
767 size_per_site = statement.ColumnInt64(0);
768 }
769 return base::ok(size_per_site);
770 }
771
772 SQLitePersistentSharedDictionaryStore::DictionaryListOrError
GetDictionariesImpl(const SharedDictionaryIsolationKey & isolation_key)773 SQLitePersistentSharedDictionaryStore::Backend::GetDictionariesImpl(
774 const SharedDictionaryIsolationKey& isolation_key) {
775 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
776 std::vector<SharedDictionaryInfo> result;
777
778 if (!InitializeDatabase()) {
779 return base::unexpected(Error::kFailedToInitializeDatabase);
780 }
781
782 // Commit `pending_last_used_time_updates_`.
783 DoCommit();
784
785 static constexpr char kQuery[] =
786 // clang-format off
787 "SELECT "
788 "primary_key,"
789 "match,"
790 "match_dest,"
791 "id,"
792 "url,"
793 "res_time,"
794 "exp_time,"
795 "last_used_time,"
796 "size,"
797 "sha256,"
798 "token_high,"
799 "token_low FROM dictionaries "
800 "WHERE frame_origin=? AND top_frame_site=? "
801 "ORDER BY primary_key";
802 // clang-format on
803
804 if (!db()->IsSQLValid(kQuery)) {
805 return base::unexpected(Error::kInvalidSql);
806 }
807
808 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
809 statement.BindString(0, isolation_key.frame_origin().Serialize());
810 statement.BindString(1, isolation_key.top_frame_site().Serialize());
811
812 while (statement.Step()) {
813 const int64_t primary_key_in_database = statement.ColumnInt64(0);
814 const std::string match = statement.ColumnString(1);
815 const std::string match_dest = statement.ColumnString(2);
816 const std::string id = statement.ColumnString(3);
817 const std::string url_string = statement.ColumnString(4);
818 const base::Time response_time = statement.ColumnTime(5);
819 const base::Time expiration_time = statement.ColumnTime(6);
820 const base::Time last_used_time = statement.ColumnTime(7);
821 const size_t size = statement.ColumnInt64(8);
822
823 std::optional<SHA256HashValue> sha256_hash =
824 ToSHA256HashValue(statement.ColumnBlob(9));
825 if (!sha256_hash) {
826 LOG(WARNING) << "Invalid hash";
827 continue;
828 }
829 std::optional<base::UnguessableToken> disk_cache_key_token =
830 ToUnguessableToken(statement.ColumnInt64(10),
831 statement.ColumnInt64(11));
832 if (!disk_cache_key_token) {
833 LOG(WARNING) << "Invalid token";
834 continue;
835 }
836 result.emplace_back(GURL(url_string), response_time,
837 expiration_time - response_time, match, match_dest, id,
838 last_used_time, size, *sha256_hash,
839 *disk_cache_key_token, primary_key_in_database);
840 }
841 return base::ok(std::move(result));
842 }
843
844 SQLitePersistentSharedDictionaryStore::DictionaryMapOrError
GetAllDictionariesImpl()845 SQLitePersistentSharedDictionaryStore::Backend::GetAllDictionariesImpl() {
846 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
847 if (!InitializeDatabase()) {
848 return base::unexpected(Error::kFailedToInitializeDatabase);
849 }
850
851 static constexpr char kQuery[] =
852 // clang-format off
853 "SELECT "
854 "primary_key,"
855 "frame_origin,"
856 "top_frame_site,"
857 "match,"
858 "match_dest,"
859 "id,"
860 "url,"
861 "res_time,"
862 "exp_time,"
863 "last_used_time,"
864 "size,"
865 "sha256,"
866 "token_high,"
867 "token_low FROM dictionaries "
868 "ORDER BY primary_key";
869 // clang-format on
870
871 if (!db()->IsSQLValid(kQuery)) {
872 return base::unexpected(Error::kInvalidSql);
873 }
874
875 std::map<SharedDictionaryIsolationKey, std::vector<SharedDictionaryInfo>>
876 result;
877 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
878
879 while (statement.Step()) {
880 const int64_t primary_key_in_database = statement.ColumnInt64(0);
881 const std::string frame_origin_string = statement.ColumnString(1);
882 const std::string top_frame_site_string = statement.ColumnString(2);
883 const std::string match = statement.ColumnString(3);
884 const std::string match_dest = statement.ColumnString(4);
885 const std::string id = statement.ColumnString(5);
886 const std::string url_string = statement.ColumnString(6);
887 const base::Time response_time = statement.ColumnTime(7);
888 const base::Time expiration_time = statement.ColumnTime(8);
889 const base::Time last_used_time = statement.ColumnTime(9);
890 const size_t size = statement.ColumnInt64(10);
891
892 std::optional<SHA256HashValue> sha256_hash =
893 ToSHA256HashValue(statement.ColumnBlob(11));
894 if (!sha256_hash) {
895 LOG(WARNING) << "Invalid hash";
896 continue;
897 }
898
899 std::optional<base::UnguessableToken> disk_cache_key_token =
900 ToUnguessableToken(statement.ColumnInt64(12),
901 statement.ColumnInt64(13));
902 if (!disk_cache_key_token) {
903 LOG(WARNING) << "Invalid token";
904 continue;
905 }
906
907 url::Origin frame_origin = url::Origin::Create(GURL(frame_origin_string));
908 SchemefulSite top_frame_site = SchemefulSite(GURL(top_frame_site_string));
909
910 result[SharedDictionaryIsolationKey(frame_origin, top_frame_site)]
911 .emplace_back(GURL(url_string), response_time,
912 expiration_time - response_time, match, match_dest, id,
913 last_used_time, size, *sha256_hash, *disk_cache_key_token,
914 primary_key_in_database);
915 }
916 return base::ok(std::move(result));
917 }
918
919 SQLitePersistentSharedDictionaryStore::UsageInfoOrError
GetUsageInfoImpl()920 SQLitePersistentSharedDictionaryStore::Backend::GetUsageInfoImpl() {
921 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
922 if (!InitializeDatabase()) {
923 return base::unexpected(Error::kFailedToInitializeDatabase);
924 }
925
926 static constexpr char kQuery[] =
927 // clang-format off
928 "SELECT "
929 "frame_origin,"
930 "top_frame_site,"
931 "size FROM dictionaries "
932 "ORDER BY primary_key";
933 // clang-format on
934
935 if (!db()->IsSQLValid(kQuery)) {
936 return base::unexpected(Error::kInvalidSql);
937 }
938
939 std::map<SharedDictionaryIsolationKey, SharedDictionaryUsageInfo> result_map;
940 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
941
942 while (statement.Step()) {
943 const std::string frame_origin_string = statement.ColumnString(0);
944 const std::string top_frame_site_string = statement.ColumnString(1);
945 const size_t size = statement.ColumnInt64(2);
946
947 const SharedDictionaryIsolationKey key = SharedDictionaryIsolationKey(
948 url::Origin::Create(GURL(frame_origin_string)),
949 SchemefulSite(GURL(top_frame_site_string)));
950 auto it = result_map.find(key);
951 if (it != result_map.end()) {
952 it->second.total_size_bytes += size;
953 } else {
954 result_map[key] = SharedDictionaryUsageInfo{.isolation_key = key,
955 .total_size_bytes = size};
956 }
957 }
958
959 std::vector<SharedDictionaryUsageInfo> result;
960 for (auto& it : result_map) {
961 result.push_back(std::move(it.second));
962 }
963 return base::ok(std::move(result));
964 }
965
966 SQLitePersistentSharedDictionaryStore::OriginListOrError
GetOriginsBetweenImpl(const base::Time start_time,const base::Time end_time)967 SQLitePersistentSharedDictionaryStore::Backend::GetOriginsBetweenImpl(
968 const base::Time start_time,
969 const base::Time end_time) {
970 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
971 if (!InitializeDatabase()) {
972 return base::unexpected(Error::kFailedToInitializeDatabase);
973 }
974
975 static constexpr char kQuery[] =
976 // clang-format off
977 "SELECT "
978 "frame_origin FROM dictionaries "
979 "WHERE res_time>=? AND res_time<? "
980 "ORDER BY primary_key";
981 // clang-format on
982
983 if (!db()->IsSQLValid(kQuery)) {
984 return base::unexpected(Error::kInvalidSql);
985 }
986
987 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
988 statement.BindTime(0, start_time);
989 statement.BindTime(1, end_time);
990
991 std::set<url::Origin> origins;
992 while (statement.Step()) {
993 const std::string frame_origin_string = statement.ColumnString(0);
994 origins.insert(url::Origin::Create(GURL(frame_origin_string)));
995 }
996 return base::ok(std::vector<url::Origin>(origins.begin(), origins.end()));
997 }
998
999 SQLitePersistentSharedDictionaryStore::UnguessableTokenSetOrError
ClearAllDictionariesImpl()1000 SQLitePersistentSharedDictionaryStore::Backend::ClearAllDictionariesImpl() {
1001 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1002
1003 if (!InitializeDatabase()) {
1004 return base::unexpected(Error::kFailedToInitializeDatabase);
1005 }
1006
1007 sql::Transaction transaction(db());
1008 if (!transaction.Begin()) {
1009 return base::unexpected(Error::kFailedToBeginTransaction);
1010 }
1011
1012 static constexpr char kQuery[] =
1013 "DELETE FROM dictionaries RETURNING token_high, token_low";
1014 if (!db()->IsSQLValid(kQuery)) {
1015 return base::unexpected(Error::kInvalidSql);
1016 }
1017 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1018
1019 std::vector<base::UnguessableToken> tokens;
1020 while (statement.Step()) {
1021 const int64_t token_high = statement.ColumnInt64(0);
1022 const int64_t token_low = statement.ColumnInt64(1);
1023 std::optional<base::UnguessableToken> disk_cache_key_token =
1024 ToUnguessableToken(token_high, token_low);
1025 if (!disk_cache_key_token) {
1026 continue;
1027 }
1028 tokens.emplace_back(*disk_cache_key_token);
1029 }
1030
1031 if (!meta_table()->SetValue(kTotalDictSizeKey, 0)) {
1032 return base::unexpected(Error::kFailedToSetTotalDictSize);
1033 }
1034
1035 if (!transaction.Commit()) {
1036 return base::unexpected(Error::kFailedToCommitTransaction);
1037 }
1038 return base::ok(
1039 std::set<base::UnguessableToken>(tokens.begin(), tokens.end()));
1040 }
1041
1042 SQLitePersistentSharedDictionaryStore::UnguessableTokenSetOrError
ClearDictionariesImpl(base::Time start_time,base::Time end_time,base::RepeatingCallback<bool (const GURL &)> url_matcher)1043 SQLitePersistentSharedDictionaryStore::Backend::ClearDictionariesImpl(
1044 base::Time start_time,
1045 base::Time end_time,
1046 base::RepeatingCallback<bool(const GURL&)> url_matcher) {
1047 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1048 if (!InitializeDatabase()) {
1049 return base::unexpected(Error::kFailedToInitializeDatabase);
1050 }
1051
1052 // Commit `pending_last_used_time_updates_`.
1053 DoCommit();
1054
1055 sql::Transaction transaction(db());
1056 if (!transaction.Begin()) {
1057 return base::unexpected(Error::kFailedToBeginTransaction);
1058 }
1059 std::vector<int64_t> primary_keys;
1060 std::vector<base::UnguessableToken> tokens;
1061 int64_t total_size = 0;
1062 Error error = url_matcher ? SelectMatchingDictionariesWithUrlMatcher(
1063 start_time, end_time, std::move(url_matcher),
1064 &primary_keys, &tokens, &total_size)
1065 : SelectMatchingDictionaries(start_time, end_time,
1066 &primary_keys, &tokens,
1067 &total_size);
1068 if (error != Error::kOk) {
1069 return base::unexpected(error);
1070 }
1071 for (int64_t primary_key : primary_keys) {
1072 error = DeleteDictionaryByPrimaryKey(primary_key);
1073 if (error != Error::kOk) {
1074 return base::unexpected(error);
1075 }
1076 }
1077 if (total_size != 0) {
1078 uint64_t total_dictionary_size = 0;
1079 error = UpdateTotalDictionarySizeInMetaTable(-total_size,
1080 &total_dictionary_size);
1081 if (error != Error::kOk) {
1082 return base::unexpected(error);
1083 }
1084 }
1085
1086 transaction.Commit();
1087 return base::ok(
1088 std::set<base::UnguessableToken>(tokens.begin(), tokens.end()));
1089 }
1090
1091 SQLitePersistentSharedDictionaryStore::Error
SelectMatchingDictionaries(base::Time start_time,base::Time end_time,std::vector<int64_t> * primary_keys_out,std::vector<base::UnguessableToken> * tokens_out,int64_t * total_size_out)1092 SQLitePersistentSharedDictionaryStore::Backend::SelectMatchingDictionaries(
1093 base::Time start_time,
1094 base::Time end_time,
1095 std::vector<int64_t>* primary_keys_out,
1096 std::vector<base::UnguessableToken>* tokens_out,
1097 int64_t* total_size_out) {
1098 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1099 static constexpr char kQuery[] =
1100 // clang-format off
1101 "SELECT "
1102 "primary_key,"
1103 "size,"
1104 "token_high,"
1105 "token_low FROM dictionaries "
1106 "WHERE res_time>=? AND res_time<? "
1107 "ORDER BY primary_key";
1108 // clang-format on
1109
1110 if (!db()->IsSQLValid(kQuery)) {
1111 return Error::kInvalidSql;
1112 }
1113
1114 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1115 statement.BindTime(0, start_time);
1116 statement.BindTime(1, end_time.is_null() ? base::Time::Max() : end_time);
1117
1118 base::CheckedNumeric<int64_t> checked_total_size;
1119 while (statement.Step()) {
1120 const int64_t primary_key_in_database = statement.ColumnInt64(0);
1121 const size_t size = statement.ColumnInt64(1);
1122 const int64_t token_high = statement.ColumnInt64(2);
1123 const int64_t token_low = statement.ColumnInt64(3);
1124 std::optional<base::UnguessableToken> disk_cache_key_token =
1125 ToUnguessableToken(token_high, token_low);
1126 if (!disk_cache_key_token) {
1127 LOG(WARNING) << "Invalid token";
1128 continue;
1129 }
1130 primary_keys_out->emplace_back(primary_key_in_database);
1131 tokens_out->emplace_back(*disk_cache_key_token);
1132 checked_total_size += size;
1133 }
1134 *total_size_out = checked_total_size.ValueOrDie();
1135 return Error::kOk;
1136 }
1137
1138 SQLitePersistentSharedDictionaryStore::Error
1139 SQLitePersistentSharedDictionaryStore::Backend::
SelectMatchingDictionariesWithUrlMatcher(base::Time start_time,base::Time end_time,base::RepeatingCallback<bool (const GURL &)> url_matcher,std::vector<int64_t> * primary_keys_out,std::vector<base::UnguessableToken> * tokens_out,int64_t * total_size_out)1140 SelectMatchingDictionariesWithUrlMatcher(
1141 base::Time start_time,
1142 base::Time end_time,
1143 base::RepeatingCallback<bool(const GURL&)> url_matcher,
1144 std::vector<int64_t>* primary_keys_out,
1145 std::vector<base::UnguessableToken>* tokens_out,
1146 int64_t* total_size_out) {
1147 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1148 static constexpr char kQuery[] =
1149 // clang-format off
1150 "SELECT "
1151 "primary_key,"
1152 "frame_origin,"
1153 "top_frame_site,"
1154 "host,"
1155 "size,"
1156 "token_high,"
1157 "token_low FROM dictionaries "
1158 "WHERE res_time>=? AND res_time<? "
1159 "ORDER BY primary_key";
1160 // clang-format on
1161
1162 if (!db()->IsSQLValid(kQuery)) {
1163 return Error::kInvalidSql;
1164 }
1165
1166 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1167 statement.BindTime(0, start_time);
1168 statement.BindTime(1, end_time.is_null() ? base::Time::Max() : end_time);
1169
1170 base::CheckedNumeric<int64_t> checked_total_size;
1171 while (statement.Step()) {
1172 const int64_t primary_key_in_database = statement.ColumnInt64(0);
1173 const std::string frame_origin_string = statement.ColumnString(1);
1174 const std::string top_frame_site_string = statement.ColumnString(2);
1175 const std::string host = statement.ColumnString(3);
1176 const size_t size = statement.ColumnInt64(4);
1177 const int64_t token_high = statement.ColumnInt64(5);
1178 const int64_t token_low = statement.ColumnInt64(6);
1179
1180 if (!url_matcher.Run(GURL(frame_origin_string)) &&
1181 !url_matcher.Run(GURL(top_frame_site_string)) &&
1182 !url_matcher.Run(GURL(host))) {
1183 continue;
1184 }
1185 std::optional<base::UnguessableToken> disk_cache_key_token =
1186 ToUnguessableToken(token_high, token_low);
1187 if (!disk_cache_key_token) {
1188 LOG(WARNING) << "Invalid token";
1189 continue;
1190 }
1191 primary_keys_out->emplace_back(primary_key_in_database);
1192 tokens_out->emplace_back(*disk_cache_key_token);
1193 checked_total_size += size;
1194 }
1195 *total_size_out = checked_total_size.ValueOrDie();
1196 return Error::kOk;
1197 }
1198
1199 SQLitePersistentSharedDictionaryStore::UnguessableTokenSetOrError
1200 SQLitePersistentSharedDictionaryStore::Backend::
ClearDictionariesForIsolationKeyImpl(const SharedDictionaryIsolationKey & isolation_key)1201 ClearDictionariesForIsolationKeyImpl(
1202 const SharedDictionaryIsolationKey& isolation_key) {
1203 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1204 if (!InitializeDatabase()) {
1205 return base::unexpected(Error::kFailedToInitializeDatabase);
1206 }
1207 sql::Transaction transaction(db());
1208 if (!transaction.Begin()) {
1209 return base::unexpected(Error::kFailedToBeginTransaction);
1210 }
1211
1212 static constexpr char kQuery[] =
1213 // clang-format off
1214 "DELETE FROM dictionaries "
1215 "WHERE frame_origin=? AND top_frame_site=? "
1216 "RETURNING size, token_high, token_low";
1217 // clang-format on
1218
1219 if (!db()->IsSQLValid(kQuery)) {
1220 return base::unexpected(Error::kInvalidSql);
1221 }
1222
1223 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1224 statement.BindString(0, isolation_key.frame_origin().Serialize());
1225 statement.BindString(1, isolation_key.top_frame_site().Serialize());
1226
1227 std::vector<base::UnguessableToken> tokens;
1228 base::CheckedNumeric<int64_t> checked_total_size = 0;
1229 while (statement.Step()) {
1230 const size_t size = statement.ColumnInt64(0);
1231 const int64_t token_high = statement.ColumnInt64(1);
1232 const int64_t token_low = statement.ColumnInt64(2);
1233
1234 checked_total_size += size;
1235
1236 std::optional<base::UnguessableToken> disk_cache_key_token =
1237 ToUnguessableToken(token_high, token_low);
1238 if (!disk_cache_key_token) {
1239 continue;
1240 }
1241 tokens.emplace_back(*disk_cache_key_token);
1242 }
1243
1244 int64_t total_size = checked_total_size.ValueOrDie();
1245 if (total_size != 0) {
1246 uint64_t total_dictionary_size = 0;
1247 Error error = UpdateTotalDictionarySizeInMetaTable(-total_size,
1248 &total_dictionary_size);
1249 if (error != Error::kOk) {
1250 return base::unexpected(error);
1251 }
1252 }
1253 transaction.Commit();
1254 return base::ok(
1255 std::set<base::UnguessableToken>(tokens.begin(), tokens.end()));
1256 }
1257
1258 SQLitePersistentSharedDictionaryStore::UnguessableTokenSetOrError
DeleteExpiredDictionariesImpl(base::Time now)1259 SQLitePersistentSharedDictionaryStore::Backend::DeleteExpiredDictionariesImpl(
1260 base::Time now) {
1261 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1262 if (!InitializeDatabase()) {
1263 return base::unexpected(Error::kFailedToInitializeDatabase);
1264 }
1265 sql::Transaction transaction(db());
1266 if (!transaction.Begin()) {
1267 return base::unexpected(Error::kFailedToBeginTransaction);
1268 }
1269 static constexpr char kQuery[] =
1270 // clang-format off
1271 "DELETE FROM dictionaries "
1272 "WHERE exp_time<=? "
1273 "RETURNING size, token_high, token_low";
1274 // clang-format on
1275
1276 if (!db()->IsSQLValid(kQuery)) {
1277 return base::unexpected(Error::kInvalidSql);
1278 }
1279
1280 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1281 statement.BindTime(0, now);
1282
1283 std::vector<base::UnguessableToken> tokens;
1284 base::CheckedNumeric<int64_t> checked_total_size = 0;
1285 while (statement.Step()) {
1286 const size_t size = statement.ColumnInt64(0);
1287 const int64_t token_high = statement.ColumnInt64(1);
1288 const int64_t token_low = statement.ColumnInt64(2);
1289
1290 checked_total_size += size;
1291
1292 std::optional<base::UnguessableToken> disk_cache_key_token =
1293 ToUnguessableToken(token_high, token_low);
1294 if (!disk_cache_key_token) {
1295 LOG(WARNING) << "Invalid token";
1296 continue;
1297 }
1298 tokens.emplace_back(*disk_cache_key_token);
1299 }
1300
1301 int64_t total_size = checked_total_size.ValueOrDie();
1302 if (total_size != 0) {
1303 uint64_t total_dictionary_size = 0;
1304 Error error = UpdateTotalDictionarySizeInMetaTable(-total_size,
1305 &total_dictionary_size);
1306 if (error != Error::kOk) {
1307 return base::unexpected(error);
1308 }
1309 }
1310 transaction.Commit();
1311 return base::ok(
1312 std::set<base::UnguessableToken>(tokens.begin(), tokens.end()));
1313 }
1314
1315 SQLitePersistentSharedDictionaryStore::UnguessableTokenSetOrError
ProcessEvictionImpl(uint64_t cache_max_size,uint64_t size_low_watermark,uint64_t cache_max_count,uint64_t count_low_watermark)1316 SQLitePersistentSharedDictionaryStore::Backend::ProcessEvictionImpl(
1317 uint64_t cache_max_size,
1318 uint64_t size_low_watermark,
1319 uint64_t cache_max_count,
1320 uint64_t count_low_watermark) {
1321 if (!InitializeDatabase()) {
1322 return base::unexpected(Error::kFailedToInitializeDatabase);
1323 }
1324
1325 // Commit `pending_last_used_time_updates_`.
1326 DoCommit();
1327
1328 sql::Transaction transaction(db());
1329 if (!transaction.Begin()) {
1330 return base::unexpected(Error::kFailedToBeginTransaction);
1331 }
1332 std::vector<int64_t> primary_keys;
1333 std::vector<base::UnguessableToken> tokens;
1334 int64_t total_size_after_eviction = 0;
1335 Error error = SelectEvictionCandidates(
1336 cache_max_size, size_low_watermark, cache_max_count, count_low_watermark,
1337 &primary_keys, &tokens, &total_size_after_eviction);
1338 if (error != Error::kOk) {
1339 return base::unexpected(error);
1340 }
1341 CHECK_EQ(primary_keys.size(), tokens.size());
1342 if (primary_keys.empty()) {
1343 return base::ok(std::set<base::UnguessableToken>());
1344 }
1345 for (int64_t primary_key : primary_keys) {
1346 error = DeleteDictionaryByPrimaryKey(primary_key);
1347 if (error != Error::kOk) {
1348 return base::unexpected(error);
1349 }
1350 }
1351
1352 if (!meta_table()->SetValue(kTotalDictSizeKey, total_size_after_eviction)) {
1353 return base::unexpected(Error::kFailedToSetTotalDictSize);
1354 }
1355
1356 transaction.Commit();
1357 return base::ok(
1358 std::set<base::UnguessableToken>(tokens.begin(), tokens.end()));
1359 }
1360
1361 SQLitePersistentSharedDictionaryStore::Error
SelectEvictionCandidates(uint64_t cache_max_size,uint64_t size_low_watermark,uint64_t cache_max_count,uint64_t count_low_watermark,std::vector<int64_t> * primary_keys_out,std::vector<base::UnguessableToken> * tokens_out,int64_t * total_size_after_eviction_out)1362 SQLitePersistentSharedDictionaryStore::Backend::SelectEvictionCandidates(
1363 uint64_t cache_max_size,
1364 uint64_t size_low_watermark,
1365 uint64_t cache_max_count,
1366 uint64_t count_low_watermark,
1367 std::vector<int64_t>* primary_keys_out,
1368 std::vector<base::UnguessableToken>* tokens_out,
1369 int64_t* total_size_after_eviction_out) {
1370 ASSIGN_OR_RETURN(uint64_t total_dictionary_size,
1371 GetTotalDictionarySizeImpl());
1372 ASSIGN_OR_RETURN(uint64_t total_dictionary_count, GetTotalDictionaryCount());
1373
1374 if ((cache_max_size == 0 || total_dictionary_size <= cache_max_size) &&
1375 total_dictionary_count <= cache_max_count) {
1376 return Error::kOk;
1377 }
1378
1379 uint64_t to_be_removed_count = 0;
1380 if (total_dictionary_count > count_low_watermark) {
1381 to_be_removed_count = total_dictionary_count - count_low_watermark;
1382 }
1383
1384 base::CheckedNumeric<uint64_t> checked_total_dictionary_size =
1385 total_dictionary_size;
1386
1387 static constexpr char kQuery[] =
1388 // clang-format off
1389 "SELECT "
1390 "primary_key,"
1391 "size,"
1392 "token_high,"
1393 "token_low FROM dictionaries "
1394 "ORDER BY last_used_time";
1395 // clang-format on
1396
1397 if (!db()->IsSQLValid(kQuery)) {
1398 return Error::kInvalidSql;
1399 }
1400
1401 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1402 while (statement.Step()) {
1403 const int64_t primary_key_in_database = statement.ColumnInt64(0);
1404 const size_t size = statement.ColumnInt64(1);
1405 const int64_t token_high = statement.ColumnInt64(2);
1406 const int64_t token_low = statement.ColumnInt64(3);
1407 std::optional<base::UnguessableToken> disk_cache_key_token =
1408 ToUnguessableToken(token_high, token_low);
1409 if (!disk_cache_key_token) {
1410 LOG(WARNING) << "Invalid token";
1411 continue;
1412 }
1413 checked_total_dictionary_size -= size;
1414
1415 if (!checked_total_dictionary_size.IsValid()) {
1416 base::debug::DumpWithoutCrashing();
1417 return Error::kInvalidTotalDictSize;
1418 }
1419
1420 *total_size_after_eviction_out =
1421 base::checked_cast<int64_t>(checked_total_dictionary_size.ValueOrDie());
1422 primary_keys_out->emplace_back(primary_key_in_database);
1423 tokens_out->emplace_back(*disk_cache_key_token);
1424
1425 if ((cache_max_size == 0 ||
1426 size_low_watermark >= checked_total_dictionary_size.ValueOrDie()) &&
1427 tokens_out->size() >= to_be_removed_count) {
1428 break;
1429 }
1430 }
1431 return Error::kOk;
1432 }
1433
1434 SQLitePersistentSharedDictionaryStore::Error
DeleteDictionaryByPrimaryKey(int64_t primary_key)1435 SQLitePersistentSharedDictionaryStore::Backend::DeleteDictionaryByPrimaryKey(
1436 int64_t primary_key) {
1437 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1438 static constexpr char kQuery[] =
1439 "DELETE FROM dictionaries WHERE primary_key=?";
1440 if (!db()->IsSQLValid(kQuery)) {
1441 return Error::kInvalidSql;
1442 }
1443 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1444 statement.BindInt64(0, primary_key);
1445
1446 if (!statement.Run()) {
1447 return Error::kFailedToExecuteSql;
1448 }
1449 return Error::kOk;
1450 }
1451
1452 SQLitePersistentSharedDictionaryStore::Error
1453 SQLitePersistentSharedDictionaryStore::Backend::
DeleteDictionariesByDiskCacheKeyTokensImpl(const std::set<base::UnguessableToken> & disk_cache_key_tokens)1454 DeleteDictionariesByDiskCacheKeyTokensImpl(
1455 const std::set<base::UnguessableToken>& disk_cache_key_tokens) {
1456 if (!InitializeDatabase()) {
1457 return Error::kFailedToInitializeDatabase;
1458 }
1459
1460 sql::Transaction transaction(db());
1461 if (!transaction.Begin()) {
1462 return Error::kFailedToBeginTransaction;
1463 }
1464
1465 base::CheckedNumeric<int64_t> checked_total_deleted_dictionary_size;
1466 for (const auto& token : disk_cache_key_tokens) {
1467 ASSIGN_OR_RETURN(uint64_t deleted_dictionary_size,
1468 DeleteDictionaryByDiskCacheToken(token));
1469 checked_total_deleted_dictionary_size += deleted_dictionary_size;
1470 }
1471
1472 int64_t total_deleted_dictionary_size =
1473 checked_total_deleted_dictionary_size.ValueOrDie();
1474 if (total_deleted_dictionary_size != 0) {
1475 uint64_t total_dictionary_size = 0;
1476 Error error = UpdateTotalDictionarySizeInMetaTable(
1477 -total_deleted_dictionary_size, &total_dictionary_size);
1478 if (error != Error::kOk) {
1479 return error;
1480 }
1481 }
1482
1483 if (!transaction.Commit()) {
1484 return Error::kFailedToCommitTransaction;
1485 }
1486 return Error::kOk;
1487 }
1488
1489 base::expected<uint64_t, SQLitePersistentSharedDictionaryStore::Error>
1490 SQLitePersistentSharedDictionaryStore::Backend::
DeleteDictionaryByDiskCacheToken(const base::UnguessableToken & disk_cache_key_token)1491 DeleteDictionaryByDiskCacheToken(
1492 const base::UnguessableToken& disk_cache_key_token) {
1493 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1494 if (!InitializeDatabase()) {
1495 return base::unexpected(Error::kFailedToInitializeDatabase);
1496 }
1497 static constexpr char kQuery[] =
1498 // clang-format off
1499 "DELETE FROM dictionaries "
1500 "WHERE token_high=? AND token_low=?"
1501 "RETURNING size";
1502 // clang-format on
1503
1504 if (!db()->IsSQLValid(kQuery)) {
1505 return base::unexpected(Error::kInvalidSql);
1506 }
1507
1508 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1509 // There is no `sql::Statement::BindUint64()` method. So we cast to int64_t.
1510 int64_t token_high =
1511 static_cast<int64_t>(disk_cache_key_token.GetHighForSerialization());
1512 int64_t token_low =
1513 static_cast<int64_t>(disk_cache_key_token.GetLowForSerialization());
1514 statement.BindInt64(0, token_high);
1515 statement.BindInt64(1, token_low);
1516
1517 base::CheckedNumeric<uint64_t> checked_size = 0;
1518 while (statement.Step()) {
1519 const size_t size = statement.ColumnInt64(0);
1520 checked_size += size;
1521 }
1522 return base::ok(checked_size.ValueOrDie());
1523 }
1524
1525 SQLitePersistentSharedDictionaryStore::UnguessableTokenSetOrError
GetAllDiskCacheKeyTokensImpl()1526 SQLitePersistentSharedDictionaryStore::Backend::GetAllDiskCacheKeyTokensImpl() {
1527 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1528 if (!InitializeDatabase()) {
1529 return base::unexpected(Error::kFailedToInitializeDatabase);
1530 }
1531
1532 static constexpr char kQuery[] =
1533 // clang-format off
1534 "SELECT "
1535 "primary_key,"
1536 "token_high,"
1537 "token_low FROM dictionaries "
1538 "ORDER BY primary_key";
1539 // clang-format on
1540
1541 if (!db()->IsSQLValid(kQuery)) {
1542 return base::unexpected(Error::kInvalidSql);
1543 }
1544
1545 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1546 std::vector<base::UnguessableToken> tokens;
1547 while (statement.Step()) {
1548 std::optional<base::UnguessableToken> disk_cache_key_token =
1549 ToUnguessableToken(statement.ColumnInt64(1), statement.ColumnInt64(2));
1550 if (!disk_cache_key_token) {
1551 LOG(WARNING) << "Invalid token";
1552 continue;
1553 }
1554 tokens.emplace_back(*disk_cache_key_token);
1555 }
1556 return base::ok(
1557 std::set<base::UnguessableToken>(tokens.begin(), tokens.end()));
1558 }
1559
1560 void SQLitePersistentSharedDictionaryStore::Backend::
UpdateDictionaryLastUsedTime(int64_t primary_key_in_database,base::Time last_used_time)1561 UpdateDictionaryLastUsedTime(int64_t primary_key_in_database,
1562 base::Time last_used_time) {
1563 CHECK(client_task_runner()->RunsTasksInCurrentSequence());
1564 CHECK(!background_task_runner()->RunsTasksInCurrentSequence());
1565 size_t num_pending;
1566 {
1567 base::AutoLock locked(lock_);
1568 pending_last_used_time_updates_[primary_key_in_database] = last_used_time;
1569 num_pending = ++num_pending_;
1570 }
1571 // Commit every 30 seconds.
1572 static const int kCommitIntervalMs = 30 * 1000;
1573 // Commit right away if we have more than 100 operations.
1574 static const size_t kCommitAfterBatchSize = 100;
1575 if (num_pending == 1) {
1576 // We've gotten our first entry for this batch, fire off the timer.
1577 if (!background_task_runner()->PostDelayedTask(
1578 FROM_HERE, base::BindOnce(&Backend::Commit, this),
1579 base::Milliseconds(kCommitIntervalMs))) {
1580 NOTREACHED() << "background_task_runner_ is not running.";
1581 }
1582 } else if (num_pending >= kCommitAfterBatchSize) {
1583 // We've reached a big enough batch, fire off a commit now.
1584 PostBackgroundTask(FROM_HERE, base::BindOnce(&Backend::Commit, this));
1585 }
1586 }
1587
1588 base::expected<uint64_t, SQLitePersistentSharedDictionaryStore::Error>
GetTotalDictionaryCount()1589 SQLitePersistentSharedDictionaryStore::Backend::GetTotalDictionaryCount() {
1590 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1591 static constexpr char kQuery[] =
1592 "SELECT COUNT(primary_key) FROM dictionaries";
1593
1594 if (!db()->IsSQLValid(kQuery)) {
1595 return base::unexpected(Error::kInvalidSql);
1596 }
1597 uint64_t dictionary_count = 0;
1598 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1599 if (statement.Step()) {
1600 dictionary_count = statement.ColumnInt64(0);
1601 }
1602 return base::ok(dictionary_count);
1603 }
1604
1605 bool SQLitePersistentSharedDictionaryStore::Backend::
GetExistingDictionarySizeAndDiskCacheKeyToken(const SharedDictionaryIsolationKey & isolation_key,const url::SchemeHostPort & host,const std::string & match,const std::string & match_dest,int64_t * size_out,std::optional<base::UnguessableToken> * disk_cache_key_out)1606 GetExistingDictionarySizeAndDiskCacheKeyToken(
1607 const SharedDictionaryIsolationKey& isolation_key,
1608 const url::SchemeHostPort& host,
1609 const std::string& match,
1610 const std::string& match_dest,
1611 int64_t* size_out,
1612 std::optional<base::UnguessableToken>* disk_cache_key_out) {
1613 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1614
1615 static constexpr char kQuery[] =
1616 // clang-format off
1617 "SELECT "
1618 "size,"
1619 "token_high,"
1620 "token_low FROM dictionaries "
1621 "WHERE "
1622 "frame_origin=? AND "
1623 "top_frame_site=? AND "
1624 "host=? AND "
1625 "match=? AND "
1626 "match_dest=? "
1627 "ORDER BY primary_key";
1628 // clang-format on
1629
1630 if (!db()->IsSQLValid(kQuery)) {
1631 return false;
1632 }
1633 sql::Statement statement(db()->GetCachedStatement(SQL_FROM_HERE, kQuery));
1634 statement.BindString(0, isolation_key.frame_origin().Serialize());
1635 statement.BindString(1, isolation_key.top_frame_site().Serialize());
1636 statement.BindString(2, host.Serialize());
1637 statement.BindString(3, match);
1638 statement.BindString(4, match_dest);
1639
1640 if (statement.Step()) {
1641 *size_out = statement.ColumnInt64(0);
1642 *disk_cache_key_out =
1643 ToUnguessableToken(statement.ColumnInt64(1), statement.ColumnInt64(2));
1644 return true;
1645 }
1646 return false;
1647 }
1648
1649 SQLitePersistentSharedDictionaryStore::Error
1650 SQLitePersistentSharedDictionaryStore::Backend::
UpdateTotalDictionarySizeInMetaTable(int64_t size_delta,uint64_t * total_dictionary_size_out)1651 UpdateTotalDictionarySizeInMetaTable(int64_t size_delta,
1652 uint64_t* total_dictionary_size_out) {
1653 CHECK(background_task_runner()->RunsTasksInCurrentSequence());
1654 ASSIGN_OR_RETURN(uint64_t total_dictionary_size,
1655 GetTotalDictionarySizeImpl());
1656 base::CheckedNumeric<uint64_t> checked_total_dictionary_size =
1657 total_dictionary_size;
1658 checked_total_dictionary_size += size_delta;
1659 if (!checked_total_dictionary_size.IsValid()) {
1660 LOG(ERROR) << "Invalid total_dict_size detected.";
1661 base::debug::DumpWithoutCrashing();
1662 return Error::kInvalidTotalDictSize;
1663 }
1664 *total_dictionary_size_out = checked_total_dictionary_size.ValueOrDie();
1665 if (!meta_table()->SetValue(kTotalDictSizeKey, *total_dictionary_size_out)) {
1666 return Error::kFailedToSetTotalDictSize;
1667 }
1668 return Error::kOk;
1669 }
1670
SQLitePersistentSharedDictionaryStore(const base::FilePath & path,const scoped_refptr<base::SequencedTaskRunner> & client_task_runner,const scoped_refptr<base::SequencedTaskRunner> & background_task_runner)1671 SQLitePersistentSharedDictionaryStore::SQLitePersistentSharedDictionaryStore(
1672 const base::FilePath& path,
1673 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner,
1674 const scoped_refptr<base::SequencedTaskRunner>& background_task_runner)
1675 : backend_(base::MakeRefCounted<Backend>(path,
1676 client_task_runner,
1677 background_task_runner)) {}
1678
1679 SQLitePersistentSharedDictionaryStore::
~SQLitePersistentSharedDictionaryStore()1680 ~SQLitePersistentSharedDictionaryStore() {
1681 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1682 backend_->Close();
1683 }
1684
GetTotalDictionarySize(base::OnceCallback<void (SizeOrError)> callback)1685 void SQLitePersistentSharedDictionaryStore::GetTotalDictionarySize(
1686 base::OnceCallback<void(SizeOrError)> callback) {
1687 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1688 backend_->GetTotalDictionarySize(
1689 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
1690 }
1691
RegisterDictionary(const SharedDictionaryIsolationKey & isolation_key,SharedDictionaryInfo dictionary_info,const uint64_t max_size_per_site,const uint64_t max_count_per_site,base::OnceCallback<void (RegisterDictionaryResultOrError)> callback)1692 void SQLitePersistentSharedDictionaryStore::RegisterDictionary(
1693 const SharedDictionaryIsolationKey& isolation_key,
1694 SharedDictionaryInfo dictionary_info,
1695 const uint64_t max_size_per_site,
1696 const uint64_t max_count_per_site,
1697 base::OnceCallback<void(RegisterDictionaryResultOrError)> callback) {
1698 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1699 backend_->RegisterDictionary(
1700 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1701 isolation_key, std::move(dictionary_info), max_size_per_site,
1702 max_count_per_site);
1703 }
1704
GetDictionaries(const SharedDictionaryIsolationKey & isolation_key,base::OnceCallback<void (DictionaryListOrError)> callback)1705 void SQLitePersistentSharedDictionaryStore::GetDictionaries(
1706 const SharedDictionaryIsolationKey& isolation_key,
1707 base::OnceCallback<void(DictionaryListOrError)> callback) {
1708 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1709 backend_->GetDictionaries(
1710 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1711 isolation_key);
1712 }
1713
GetAllDictionaries(base::OnceCallback<void (DictionaryMapOrError)> callback)1714 void SQLitePersistentSharedDictionaryStore::GetAllDictionaries(
1715 base::OnceCallback<void(DictionaryMapOrError)> callback) {
1716 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1717 backend_->GetAllDictionaries(
1718 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
1719 }
1720
GetUsageInfo(base::OnceCallback<void (UsageInfoOrError)> callback)1721 void SQLitePersistentSharedDictionaryStore::GetUsageInfo(
1722 base::OnceCallback<void(UsageInfoOrError)> callback) {
1723 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1724 backend_->GetUsageInfo(
1725 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
1726 }
1727
GetOriginsBetween(const base::Time start_time,const base::Time end_time,base::OnceCallback<void (OriginListOrError)> callback)1728 void SQLitePersistentSharedDictionaryStore::GetOriginsBetween(
1729 const base::Time start_time,
1730 const base::Time end_time,
1731 base::OnceCallback<void(OriginListOrError)> callback) {
1732 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1733 backend_->GetOriginsBetween(
1734 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1735 start_time, end_time);
1736 }
1737
ClearAllDictionaries(base::OnceCallback<void (UnguessableTokenSetOrError)> callback)1738 void SQLitePersistentSharedDictionaryStore::ClearAllDictionaries(
1739 base::OnceCallback<void(UnguessableTokenSetOrError)> callback) {
1740 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1741 backend_->ClearAllDictionaries(
1742 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
1743 }
1744
ClearDictionaries(const base::Time start_time,const base::Time end_time,base::RepeatingCallback<bool (const GURL &)> url_matcher,base::OnceCallback<void (UnguessableTokenSetOrError)> callback)1745 void SQLitePersistentSharedDictionaryStore::ClearDictionaries(
1746 const base::Time start_time,
1747 const base::Time end_time,
1748 base::RepeatingCallback<bool(const GURL&)> url_matcher,
1749 base::OnceCallback<void(UnguessableTokenSetOrError)> callback) {
1750 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1751 backend_->ClearDictionaries(
1752 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1753 start_time, end_time, std::move(url_matcher));
1754 }
1755
ClearDictionariesForIsolationKey(const SharedDictionaryIsolationKey & isolation_key,base::OnceCallback<void (UnguessableTokenSetOrError)> callback)1756 void SQLitePersistentSharedDictionaryStore::ClearDictionariesForIsolationKey(
1757 const SharedDictionaryIsolationKey& isolation_key,
1758 base::OnceCallback<void(UnguessableTokenSetOrError)> callback) {
1759 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1760 backend_->ClearDictionariesForIsolationKey(
1761 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1762 isolation_key);
1763 }
1764
DeleteExpiredDictionaries(const base::Time now,base::OnceCallback<void (UnguessableTokenSetOrError)> callback)1765 void SQLitePersistentSharedDictionaryStore::DeleteExpiredDictionaries(
1766 const base::Time now,
1767 base::OnceCallback<void(UnguessableTokenSetOrError)> callback) {
1768 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1769 backend_->DeleteExpiredDictionaries(
1770 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)), now);
1771 }
1772
ProcessEviction(const uint64_t cache_max_size,const uint64_t size_low_watermark,const uint64_t cache_max_count,const uint64_t count_low_watermark,base::OnceCallback<void (UnguessableTokenSetOrError)> callback)1773 void SQLitePersistentSharedDictionaryStore::ProcessEviction(
1774 const uint64_t cache_max_size,
1775 const uint64_t size_low_watermark,
1776 const uint64_t cache_max_count,
1777 const uint64_t count_low_watermark,
1778 base::OnceCallback<void(UnguessableTokenSetOrError)> callback) {
1779 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1780 backend_->ProcessEviction(
1781 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1782 cache_max_size, size_low_watermark, cache_max_count, count_low_watermark);
1783 }
1784
GetAllDiskCacheKeyTokens(base::OnceCallback<void (UnguessableTokenSetOrError)> callback)1785 void SQLitePersistentSharedDictionaryStore::GetAllDiskCacheKeyTokens(
1786 base::OnceCallback<void(UnguessableTokenSetOrError)> callback) {
1787 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1788 backend_->GetAllDiskCacheKeyTokens(
1789 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)));
1790 }
1791
1792 void SQLitePersistentSharedDictionaryStore::
DeleteDictionariesByDiskCacheKeyTokens(std::set<base::UnguessableToken> disk_cache_key_tokens,base::OnceCallback<void (Error)> callback)1793 DeleteDictionariesByDiskCacheKeyTokens(
1794 std::set<base::UnguessableToken> disk_cache_key_tokens,
1795 base::OnceCallback<void(Error)> callback) {
1796 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1797 backend_->DeleteDictionariesByDiskCacheKeyTokens(
1798 WrapCallbackWithWeakPtrCheck(GetWeakPtr(), std::move(callback)),
1799 std::move(disk_cache_key_tokens));
1800 }
1801
UpdateDictionaryLastUsedTime(int64_t primary_key_in_database,base::Time last_used_time)1802 void SQLitePersistentSharedDictionaryStore::UpdateDictionaryLastUsedTime(
1803 int64_t primary_key_in_database,
1804 base::Time last_used_time) {
1805 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1806 backend_->UpdateDictionaryLastUsedTime(primary_key_in_database,
1807 last_used_time);
1808 }
1809
1810 base::WeakPtr<SQLitePersistentSharedDictionaryStore>
GetWeakPtr()1811 SQLitePersistentSharedDictionaryStore::GetWeakPtr() {
1812 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1813 return weak_factory_.GetWeakPtr();
1814 }
1815
1816 } // namespace net
1817