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