xref: /aosp_15_r20/external/cronet/net/extras/sqlite/sqlite_persistent_shared_dictionary_store.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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