1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 "content/browser/indexed_db/indexed_db_context_impl.h"
6 
7 #include <algorithm>
8 #include <string>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/logging.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/task/post_task.h"
23 #include "base/task/thread_pool.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "base/time/default_clock.h"
26 #include "base/time/time.h"
27 #include "base/values.h"
28 #include "components/services/storage/indexed_db/leveldb/leveldb_factory.h"
29 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
30 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
31 #include "content/browser/indexed_db/indexed_db_class_factory.h"
32 #include "content/browser/indexed_db/indexed_db_connection.h"
33 #include "content/browser/indexed_db/indexed_db_database.h"
34 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
35 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
36 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
37 #include "content/browser/indexed_db/indexed_db_origin_state.h"
38 #include "content/browser/indexed_db/indexed_db_origin_state_handle.h"
39 #include "content/browser/indexed_db/indexed_db_quota_client.h"
40 #include "content/browser/indexed_db/indexed_db_tracing.h"
41 #include "content/browser/indexed_db/indexed_db_transaction.h"
42 #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
43 #include "storage/browser/database/database_util.h"
44 #include "storage/common/database/database_identifier.h"
45 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
46 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
47 #include "third_party/zlib/google/zip.h"
48 #include "url/origin.h"
49 
50 using base::DictionaryValue;
51 using base::ListValue;
52 using storage::DatabaseUtil;
53 using url::Origin;
54 
55 namespace content {
56 const base::FilePath::CharType IndexedDBContextImpl::kIndexedDBDirectory[] =
57     FILE_PATH_LITERAL("IndexedDB");
58 
59 namespace {
60 
GetTestClassFactory()61 static MockBrowserTestIndexedDBClassFactory* GetTestClassFactory() {
62   static ::base::LazyInstance<MockBrowserTestIndexedDBClassFactory>::Leaky
63       s_factory = LAZY_INSTANCE_INITIALIZER;
64   return s_factory.Pointer();
65 }
66 
GetTestIDBClassFactory()67 static IndexedDBClassFactory* GetTestIDBClassFactory() {
68   return GetTestClassFactory();
69 }
70 
IsAllowedPath(const std::vector<base::FilePath> & allowed_paths,const base::FilePath & candidate_path)71 bool IsAllowedPath(const std::vector<base::FilePath>& allowed_paths,
72                    const base::FilePath& candidate_path) {
73   for (const base::FilePath& allowed_path : allowed_paths) {
74     if (candidate_path == allowed_path || allowed_path.IsParent(candidate_path))
75       return true;
76   }
77   return false;
78 }
79 
80 // This may be called after the IndexedDBContext is destroyed.
GetAllOriginsAndPaths(const base::FilePath & indexeddb_path,std::vector<Origin> * origins,std::vector<base::FilePath> * file_paths)81 void GetAllOriginsAndPaths(const base::FilePath& indexeddb_path,
82                            std::vector<Origin>* origins,
83                            std::vector<base::FilePath>* file_paths) {
84   // TODO(jsbell): DCHECK that this is running on an IndexedDB sequence,
85   // if a global handle to it is ever available.
86   if (indexeddb_path.empty())
87     return;
88   base::FileEnumerator file_enumerator(indexeddb_path, false,
89                                        base::FileEnumerator::DIRECTORIES);
90   for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
91        file_path = file_enumerator.Next()) {
92     if (file_path.Extension() == indexed_db::kLevelDBExtension &&
93         file_path.RemoveExtension().Extension() ==
94             indexed_db::kIndexedDBExtension) {
95       std::string origin_id = file_path.BaseName()
96                                   .RemoveExtension()
97                                   .RemoveExtension()
98                                   .MaybeAsASCII();
99       origins->push_back(storage::GetOriginFromIdentifier(origin_id));
100       if (file_paths)
101         file_paths->push_back(file_path);
102     }
103   }
104 }
105 
106 }  // namespace
107 
IndexedDBContextImpl(const base::FilePath & data_path,scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,base::Clock * clock,mojo::PendingRemote<storage::mojom::BlobStorageContext> blob_storage_context,mojo::PendingRemote<storage::mojom::NativeFileSystemContext> native_file_system_context,scoped_refptr<base::SequencedTaskRunner> io_task_runner,scoped_refptr<base::SequencedTaskRunner> custom_task_runner)108 IndexedDBContextImpl::IndexedDBContextImpl(
109     const base::FilePath& data_path,
110     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
111     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
112     base::Clock* clock,
113     mojo::PendingRemote<storage::mojom::BlobStorageContext>
114         blob_storage_context,
115     mojo::PendingRemote<storage::mojom::NativeFileSystemContext>
116         native_file_system_context,
117     scoped_refptr<base::SequencedTaskRunner> io_task_runner,
118     scoped_refptr<base::SequencedTaskRunner> custom_task_runner)
119     : base::RefCountedDeleteOnSequence<IndexedDBContextImpl>(
120           custom_task_runner
121               ? custom_task_runner
122               : (base::ThreadPool::CreateSequencedTaskRunner(
123                     {base::MayBlock(), base::WithBaseSyncPrimitives(),
124                      base::TaskPriority::USER_VISIBLE,
125                      // BLOCK_SHUTDOWN to support clearing session-only storage.
126                      base::TaskShutdownBehavior::BLOCK_SHUTDOWN}))),
127       indexed_db_factory_(this),
128       force_keep_session_state_(false),
129       special_storage_policy_(special_storage_policy),
130       quota_manager_proxy_(quota_manager_proxy),
131       io_task_runner_(io_task_runner),
132       clock_(clock) {
133   IDB_TRACE("init");
134   if (!data_path.empty())
135     data_path_ = data_path.Append(kIndexedDBDirectory);
136   quota_manager_proxy->RegisterClient(
137       base::MakeRefCounted<IndexedDBQuotaClient>(this));
138 
139   // This is safe because the IndexedDBContextImpl must be destructed on the
140   // IDBTaskRunner, and this task will always happen before that.
141   if (blob_storage_context || native_file_system_context) {
142     IDBTaskRunner()->PostTask(
143         FROM_HERE,
144         base::BindOnce(
145             [](mojo::Remote<storage::mojom::BlobStorageContext>*
146                    blob_storage_context,
147                mojo::Remote<storage::mojom::NativeFileSystemContext>*
148                    native_file_system_context,
149                mojo::PendingRemote<storage::mojom::BlobStorageContext>
150                    pending_blob_storage_context,
151                mojo::PendingRemote<storage::mojom::NativeFileSystemContext>
__anon9593c4210202(mojo::Remote<storage::mojom::BlobStorageContext>* blob_storage_context, mojo::Remote<storage::mojom::NativeFileSystemContext>* native_file_system_context, mojo::PendingRemote<storage::mojom::BlobStorageContext> pending_blob_storage_context, mojo::PendingRemote<storage::mojom::NativeFileSystemContext> pending_native_file_system_context) 152                    pending_native_file_system_context) {
153               if (pending_blob_storage_context) {
154                 blob_storage_context->Bind(
155                     std::move(pending_blob_storage_context));
156               }
157               if (pending_native_file_system_context) {
158                 native_file_system_context->Bind(
159                     std::move(pending_native_file_system_context));
160               }
161             },
162             &blob_storage_context_, &native_file_system_context_,
163             std::move(blob_storage_context),
164             std::move(native_file_system_context)));
165   }
166 }
167 
Bind(mojo::PendingReceiver<storage::mojom::IndexedDBControl> control)168 void IndexedDBContextImpl::Bind(
169     mojo::PendingReceiver<storage::mojom::IndexedDBControl> control) {
170   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
171   receivers_.Add(this, std::move(control));
172 }
173 
BindIndexedDB(const url::Origin & origin,mojo::PendingReceiver<blink::mojom::IDBFactory> receiver)174 void IndexedDBContextImpl::BindIndexedDB(
175     const url::Origin& origin,
176     mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) {
177   indexed_db_factory_.AddReceiver(origin, std::move(receiver));
178 }
179 
GetUsage(GetUsageCallback usage_callback)180 void IndexedDBContextImpl::GetUsage(GetUsageCallback usage_callback) {
181   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
182   std::vector<Origin> origins = GetAllOrigins();
183   std::vector<storage::mojom::IndexedDBStorageUsageInfoPtr> result;
184   for (const auto& origin : origins) {
185     storage::mojom::IndexedDBStorageUsageInfoPtr usage_info =
186         storage::mojom::IndexedDBStorageUsageInfo::New(
187             origin, GetOriginDiskUsage(origin), GetOriginLastModified(origin));
188     result.push_back(std::move(usage_info));
189   }
190   std::move(usage_callback).Run(std::move(result));
191 }
192 
193 // Note - this is being kept async (instead of having a 'sync' version) to allow
194 // ForceClose to become asynchronous.  This is required for
195 // https://crbug.com/965142.
DeleteForOrigin(const Origin & origin,DeleteForOriginCallback callback)196 void IndexedDBContextImpl::DeleteForOrigin(const Origin& origin,
197                                            DeleteForOriginCallback callback) {
198   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
199   ForceCloseSync(origin,
200                  storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
201   if (!HasOrigin(origin)) {
202     std::move(callback).Run(true);
203     return;
204   }
205 
206   if (is_incognito()) {
207     GetOriginSet()->erase(origin);
208     origin_size_map_.erase(origin);
209     std::move(callback).Run(true);
210     return;
211   }
212 
213   base::FilePath idb_directory = GetLevelDBPath(origin);
214   EnsureDiskUsageCacheInitialized(origin);
215 
216   leveldb::Status s =
217       IndexedDBClassFactory::Get()->leveldb_factory().DestroyLevelDB(
218           idb_directory);
219   bool success = s.ok();
220   if (success)
221     success = base::DeleteFileRecursively(GetBlobStorePath(origin));
222   QueryDiskAndUpdateQuotaUsage(origin);
223   if (success) {
224     GetOriginSet()->erase(origin);
225     origin_size_map_.erase(origin);
226   }
227   std::move(callback).Run(success);
228 }
229 
ForceClose(const Origin & origin,storage::mojom::ForceCloseReason reason,base::OnceClosure closure)230 void IndexedDBContextImpl::ForceClose(const Origin& origin,
231                                       storage::mojom::ForceCloseReason reason,
232                                       base::OnceClosure closure) {
233   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
234   base::UmaHistogramEnumeration("WebCore.IndexedDB.Context.ForceCloseReason",
235                                 reason);
236   if (!HasOrigin(origin)) {
237     std::move(closure).Run();
238     return;
239   }
240 
241   if (!indexeddb_factory_.get()) {
242     std::move(closure).Run();
243     return;
244   }
245 
246   // Make a copy of origin, as the ref might go away here during the close.
247   auto origin_copy = origin;
248   indexeddb_factory_->ForceClose(
249       origin_copy,
250       reason == storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
251   DCHECK_EQ(0UL, GetConnectionCountSync(origin_copy));
252   std::move(closure).Run();
253 }
254 
GetConnectionCount(const Origin & origin,GetConnectionCountCallback callback)255 void IndexedDBContextImpl::GetConnectionCount(
256     const Origin& origin,
257     GetConnectionCountCallback callback) {
258   std::move(callback).Run(GetConnectionCountSync(origin));
259 }
260 
DownloadOriginData(const url::Origin & origin,DownloadOriginDataCallback callback)261 void IndexedDBContextImpl::DownloadOriginData(
262     const url::Origin& origin,
263     DownloadOriginDataCallback callback) {
264   // All of this must run on the IndexedDB task runner to prevent script from
265   // reopening the origin while we are zipping.
266   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
267 
268   bool success = false;
269 
270   // Make sure the database hasn't been deleted.
271   if (!HasOrigin(origin)) {
272     std::move(callback).Run(success, base::FilePath(), base::FilePath());
273     return;
274   }
275 
276   ForceCloseSync(origin,
277                  storage::mojom::ForceCloseReason::FORCE_CLOSE_INTERNALS_PAGE);
278 
279   base::ScopedTempDir temp_dir;
280   if (!temp_dir.CreateUniqueTempDir()) {
281     std::move(callback).Run(success, base::FilePath(), base::FilePath());
282     return;
283   }
284 
285   // This will need to get cleaned up after the download has completed.
286   base::FilePath temp_path = temp_dir.Take();
287 
288   std::string origin_id = storage::GetIdentifierFromOrigin(origin);
289   base::FilePath zip_path =
290       temp_path.AppendASCII(origin_id).AddExtension(FILE_PATH_LITERAL("zip"));
291 
292   std::vector<base::FilePath> paths = GetStoragePaths(origin);
293   zip::ZipWithFilterCallback(data_path(), zip_path,
294                              base::BindRepeating(IsAllowedPath, paths));
295 
296   success = true;
297   std::move(callback).Run(success, temp_path, zip_path);
298 }
299 
GetAllOriginsDetails(GetAllOriginsDetailsCallback callback)300 void IndexedDBContextImpl::GetAllOriginsDetails(
301     GetAllOriginsDetailsCallback callback) {
302   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
303   std::vector<Origin> origins = GetAllOrigins();
304 
305   std::sort(origins.begin(), origins.end());
306 
307   base::ListValue list;
308   for (const auto& origin : origins) {
309     std::unique_ptr<base::DictionaryValue> info(
310         std::make_unique<base::DictionaryValue>());
311     info->SetString("url", origin.Serialize());
312     info->SetDouble("size", static_cast<double>(GetOriginDiskUsage(origin)));
313     info->SetDouble("last_modified", GetOriginLastModified(origin).ToJsTime());
314 
315     auto paths = std::make_unique<base::ListValue>();
316     if (!is_incognito()) {
317       for (const base::FilePath& path : GetStoragePaths(origin))
318         paths->AppendString(path.value());
319     } else {
320       paths->AppendString("N/A");
321     }
322     info->Set("paths", std::move(paths));
323     info->SetDouble("connection_count", GetConnectionCountSync(origin));
324 
325     // This ends up being O(NlogN), where N = number of open databases. We
326     // iterate over all open databases to extract just those in the origin, and
327     // we're iterating over all origins in the outer loop.
328 
329     if (!indexeddb_factory_.get()) {
330       list.Append(std::move(info));
331       continue;
332     }
333     std::vector<IndexedDBDatabase*> databases =
334         indexeddb_factory_->GetOpenDatabasesForOrigin(origin);
335     // TODO(jsbell): Sort by name?
336     std::unique_ptr<base::ListValue> database_list(
337         std::make_unique<base::ListValue>());
338 
339     for (IndexedDBDatabase* db : databases) {
340       std::unique_ptr<base::DictionaryValue> db_info(
341           std::make_unique<base::DictionaryValue>());
342 
343       db_info->SetString("name", db->name());
344       db_info->SetDouble("connection_count", db->ConnectionCount());
345       db_info->SetDouble("active_open_delete", db->ActiveOpenDeleteCount());
346       db_info->SetDouble("pending_open_delete", db->PendingOpenDeleteCount());
347 
348       std::unique_ptr<base::ListValue> transaction_list(
349           std::make_unique<base::ListValue>());
350 
351       for (IndexedDBConnection* connection : db->connections()) {
352         for (const auto& transaction_id_pair : connection->transactions()) {
353           const auto* transaction = transaction_id_pair.second.get();
354           std::unique_ptr<base::DictionaryValue> transaction_info(
355               std::make_unique<base::DictionaryValue>());
356 
357           switch (transaction->mode()) {
358             case blink::mojom::IDBTransactionMode::ReadOnly:
359               transaction_info->SetString("mode", "readonly");
360               break;
361             case blink::mojom::IDBTransactionMode::ReadWrite:
362               transaction_info->SetString("mode", "readwrite");
363               break;
364             case blink::mojom::IDBTransactionMode::VersionChange:
365               transaction_info->SetString("mode", "versionchange");
366               break;
367           }
368 
369           switch (transaction->state()) {
370             case IndexedDBTransaction::CREATED:
371               transaction_info->SetString("status", "blocked");
372               break;
373             case IndexedDBTransaction::STARTED:
374               if (transaction->diagnostics().tasks_scheduled > 0)
375                 transaction_info->SetString("status", "running");
376               else
377                 transaction_info->SetString("status", "started");
378               break;
379             case IndexedDBTransaction::COMMITTING:
380               transaction_info->SetString("status", "committing");
381               break;
382             case IndexedDBTransaction::FINISHED:
383               transaction_info->SetString("status", "finished");
384               break;
385           }
386 
387           transaction_info->SetDouble("tid", transaction->id());
388           transaction_info->SetDouble(
389               "age",
390               (base::Time::Now() - transaction->diagnostics().creation_time)
391                   .InMillisecondsF());
392           transaction_info->SetDouble(
393               "runtime",
394               (base::Time::Now() - transaction->diagnostics().start_time)
395                   .InMillisecondsF());
396           transaction_info->SetDouble(
397               "tasks_scheduled", transaction->diagnostics().tasks_scheduled);
398           transaction_info->SetDouble(
399               "tasks_completed", transaction->diagnostics().tasks_completed);
400 
401           std::unique_ptr<base::ListValue> scope(
402               std::make_unique<base::ListValue>());
403           for (const auto& id : transaction->scope()) {
404             const auto& stores_it = db->metadata().object_stores.find(id);
405             if (stores_it != db->metadata().object_stores.end())
406               scope->AppendString(stores_it->second.name);
407           }
408 
409           transaction_info->Set("scope", std::move(scope));
410           transaction_list->Append(std::move(transaction_info));
411         }
412       }
413       db_info->Set("transactions", std::move(transaction_list));
414 
415       database_list->Append(std::move(db_info));
416     }
417     info->Set("databases", std::move(database_list));
418     list.Append(std::move(info));
419   }
420 
421   std::move(callback).Run(is_incognito(), std::move(list));
422 }
423 
SetForceKeepSessionState()424 void IndexedDBContextImpl::SetForceKeepSessionState() {
425   force_keep_session_state_ = true;
426 }
427 
BindTestInterface(mojo::PendingReceiver<storage::mojom::IndexedDBControlTest> receiver)428 void IndexedDBContextImpl::BindTestInterface(
429     mojo::PendingReceiver<storage::mojom::IndexedDBControlTest> receiver) {
430   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
431   test_receivers_.Add(this, std::move(receiver));
432 }
433 
AddObserver(mojo::PendingRemote<storage::mojom::IndexedDBObserver> observer)434 void IndexedDBContextImpl::AddObserver(
435     mojo::PendingRemote<storage::mojom::IndexedDBObserver> observer) {
436   IDBTaskRunner()->PostTask(
437       FROM_HERE,
438       base::BindOnce(
439           [](IndexedDBContextImpl* context,
440              mojo::PendingRemote<storage::mojom::IndexedDBObserver> observer) {
441             context->observers_.Add(std::move(observer));
442           },
443           // As |this| is destroyed on the IDBTaskRunner it is safe to post raw.
444           base::Unretained(this), std::move(observer)));
445 }
446 
GetBaseDataPathForTesting(GetBaseDataPathForTestingCallback callback)447 void IndexedDBContextImpl::GetBaseDataPathForTesting(
448     GetBaseDataPathForTestingCallback callback) {
449   std::move(callback).Run(data_path());
450 }
451 
GetFilePathForTesting(const Origin & origin,GetFilePathForTestingCallback callback)452 void IndexedDBContextImpl::GetFilePathForTesting(
453     const Origin& origin,
454     GetFilePathForTestingCallback callback) {
455   std::move(callback).Run(GetLevelDBPath(origin));
456 }
457 
ResetCachesForTesting(base::OnceClosure callback)458 void IndexedDBContextImpl::ResetCachesForTesting(base::OnceClosure callback) {
459   origin_set_.reset();
460   origin_size_map_.clear();
461   std::move(callback).Run();
462 }
463 
ForceSchemaDowngradeForTesting(const url::Origin & origin,ForceSchemaDowngradeForTestingCallback callback)464 void IndexedDBContextImpl::ForceSchemaDowngradeForTesting(
465     const url::Origin& origin,
466     ForceSchemaDowngradeForTestingCallback callback) {
467   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
468 
469   if (is_incognito() || !HasOrigin(origin)) {
470     std::move(callback).Run(false);
471     return;
472   }
473 
474   if (indexeddb_factory_.get()) {
475     indexeddb_factory_->ForceSchemaDowngrade(origin);
476     std::move(callback).Run(true);
477     return;
478   }
479   ForceCloseSync(
480       origin,
481       storage::mojom::ForceCloseReason::FORCE_SCHEMA_DOWNGRADE_INTERNALS_PAGE);
482   std::move(callback).Run(false);
483 }
484 
HasV2SchemaCorruptionForTesting(const url::Origin & origin,HasV2SchemaCorruptionForTestingCallback callback)485 void IndexedDBContextImpl::HasV2SchemaCorruptionForTesting(
486     const url::Origin& origin,
487     HasV2SchemaCorruptionForTestingCallback callback) {
488   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
489 
490   if (is_incognito() || !HasOrigin(origin)) {
491     std::move(callback).Run(
492         storage::mojom::V2SchemaCorruptionStatus::CORRUPTION_UNKNOWN);
493   }
494 
495   if (indexeddb_factory_.get()) {
496     std::move(callback).Run(
497         static_cast<storage::mojom::V2SchemaCorruptionStatus>(
498             indexeddb_factory_->HasV2SchemaCorruption(origin)));
499     return;
500   }
501   return std::move(callback).Run(
502       storage::mojom::V2SchemaCorruptionStatus::CORRUPTION_UNKNOWN);
503 }
504 
WriteToIndexedDBForTesting(const url::Origin & origin,const std::string & key,const std::string & value,base::OnceClosure callback)505 void IndexedDBContextImpl::WriteToIndexedDBForTesting(
506     const url::Origin& origin,
507     const std::string& key,
508     const std::string& value,
509     base::OnceClosure callback) {
510   IndexedDBOriginStateHandle handle;
511   leveldb::Status s;
512   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
513       GetIDBFactory()->GetOrOpenOriginFactory(origin, data_path(),
514                                               /*create_if_missing=*/true);
515   CHECK(s.ok()) << s.ToString();
516   CHECK(handle.IsHeld());
517 
518   TransactionalLevelDBDatabase* db =
519       handle.origin_state()->backing_store()->db();
520   std::string value_copy = value;
521   s = db->Put(key, &value_copy);
522   CHECK(s.ok()) << s.ToString();
523   handle.Release();
524 
525   GetIDBFactory()->ForceClose(origin, true);
526   std::move(callback).Run();
527 }
528 
GetBlobCountForTesting(const Origin & origin,GetBlobCountForTestingCallback callback)529 void IndexedDBContextImpl::GetBlobCountForTesting(
530     const Origin& origin,
531     GetBlobCountForTestingCallback callback) {
532   std::move(callback).Run(GetOriginBlobFileCount(origin));
533 }
534 
GetNextBlobNumberForTesting(const Origin & origin,int64_t database_id,GetNextBlobNumberForTestingCallback callback)535 void IndexedDBContextImpl::GetNextBlobNumberForTesting(
536     const Origin& origin,
537     int64_t database_id,
538     GetNextBlobNumberForTestingCallback callback) {
539   IndexedDBOriginStateHandle handle;
540   leveldb::Status s;
541   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
542       GetIDBFactory()->GetOrOpenOriginFactory(origin, data_path(),
543                                               /*create_if_missing=*/true);
544   CHECK(s.ok()) << s.ToString();
545   CHECK(handle.IsHeld());
546 
547   TransactionalLevelDBDatabase* db =
548       handle.origin_state()->backing_store()->db();
549 
550   const std::string key_gen_key = DatabaseMetaDataKey::Encode(
551       database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
552   std::string data;
553   bool found = false;
554   bool ok = db->Get(key_gen_key, &data, &found).ok();
555   CHECK(found);
556   CHECK(ok);
557   base::StringPiece slice(data);
558   int64_t number;
559   CHECK(DecodeVarInt(&slice, &number));
560   CHECK(DatabaseMetaDataKey::IsValidBlobNumber(number));
561 
562   std::move(callback).Run(number);
563 }
564 
GetPathForBlobForTesting(const url::Origin & origin,int64_t database_id,int64_t blob_number,GetPathForBlobForTestingCallback callback)565 void IndexedDBContextImpl::GetPathForBlobForTesting(
566     const url::Origin& origin,
567     int64_t database_id,
568     int64_t blob_number,
569     GetPathForBlobForTestingCallback callback) {
570   IndexedDBOriginStateHandle handle;
571   leveldb::Status s;
572   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
573       GetIDBFactory()->GetOrOpenOriginFactory(origin, data_path(),
574                                               /*create_if_missing=*/true);
575   CHECK(s.ok()) << s.ToString();
576   CHECK(handle.IsHeld());
577 
578   IndexedDBBackingStore* backing_store = handle.origin_state()->backing_store();
579   base::FilePath path =
580       backing_store->GetBlobFileName(database_id, blob_number);
581   std::move(callback).Run(path);
582 }
583 
CompactBackingStoreForTesting(const url::Origin & origin,base::OnceClosure callback)584 void IndexedDBContextImpl::CompactBackingStoreForTesting(
585     const url::Origin& origin,
586     base::OnceClosure callback) {
587   IndexedDBFactoryImpl* factory = GetIDBFactory();
588 
589   std::vector<IndexedDBDatabase*> databases =
590       factory->GetOpenDatabasesForOrigin(origin);
591 
592   if (!databases.empty()) {
593     // Compact the first db's backing store since all the db's are in the same
594     // backing store.
595     IndexedDBDatabase* db = databases[0];
596     IndexedDBBackingStore* backing_store = db->backing_store();
597     backing_store->Compact();
598   }
599   std::move(callback).Run();
600 }
601 
BindMockFailureSingletonForTesting(mojo::PendingReceiver<storage::mojom::MockFailureInjector> receiver)602 void IndexedDBContextImpl::BindMockFailureSingletonForTesting(
603     mojo::PendingReceiver<storage::mojom::MockFailureInjector> receiver) {
604   // Lazily instantiate the GetTestClassFactory.
605   if (!mock_failure_injector_.has_value())
606     mock_failure_injector_.emplace(GetTestClassFactory());
607 
608   // TODO(enne): this should really not be a static setter.
609   CHECK(!mock_failure_injector_->is_bound());
610   GetTestClassFactory()->Reset();
611   IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetTestIDBClassFactory);
612 
613   mock_failure_injector_->Bind(std::move(receiver));
614   mock_failure_injector_->set_disconnect_handler(base::BindOnce([]() {
615     IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(nullptr);
616   }));
617 }
618 
GetDatabaseKeysForTesting(GetDatabaseKeysForTestingCallback callback)619 void IndexedDBContextImpl::GetDatabaseKeysForTesting(
620     GetDatabaseKeysForTestingCallback callback) {
621   std::move(callback).Run(SchemaVersionKey::Encode(), DataVersionKey::Encode());
622 }
623 
ForceCloseSync(const Origin & origin,storage::mojom::ForceCloseReason reason)624 void IndexedDBContextImpl::ForceCloseSync(
625     const Origin& origin,
626     storage::mojom::ForceCloseReason reason) {
627   ForceClose(origin, reason, base::DoNothing());
628 }
629 
GetIDBFactory()630 IndexedDBFactoryImpl* IndexedDBContextImpl::GetIDBFactory() {
631   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
632   if (!indexeddb_factory_.get()) {
633     // Prime our cache of origins with existing databases so we can
634     // detect when dbs are newly created.
635     GetOriginSet();
636     indexeddb_factory_ = std::make_unique<IndexedDBFactoryImpl>(
637         this, IndexedDBClassFactory::Get(), clock_);
638   }
639   return indexeddb_factory_.get();
640 }
641 
IOTaskRunner()642 base::SequencedTaskRunner* IndexedDBContextImpl::IOTaskRunner() {
643   DCHECK(io_task_runner_.get());
644   return io_task_runner_.get();
645 }
646 
GetAllOrigins()647 std::vector<Origin> IndexedDBContextImpl::GetAllOrigins() {
648   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
649   std::set<Origin>* origins_set = GetOriginSet();
650   return std::vector<Origin>(origins_set->begin(), origins_set->end());
651 }
652 
HasOrigin(const Origin & origin)653 bool IndexedDBContextImpl::HasOrigin(const Origin& origin) {
654   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
655   std::set<Origin>* set = GetOriginSet();
656   return set->find(origin) != set->end();
657 }
658 
GetOriginBlobFileCount(const Origin & origin)659 int IndexedDBContextImpl::GetOriginBlobFileCount(const Origin& origin) {
660   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
661   int count = 0;
662   base::FileEnumerator file_enumerator(GetBlobStorePath(origin), true,
663                                        base::FileEnumerator::FILES);
664   for (base::FilePath file_path = file_enumerator.Next(); !file_path.empty();
665        file_path = file_enumerator.Next()) {
666     count++;
667   }
668   return count;
669 }
670 
GetOriginDiskUsage(const Origin & origin)671 int64_t IndexedDBContextImpl::GetOriginDiskUsage(const Origin& origin) {
672   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
673   if (!HasOrigin(origin))
674     return 0;
675 
676   EnsureDiskUsageCacheInitialized(origin);
677   return origin_size_map_[origin];
678 }
679 
GetOriginLastModified(const Origin & origin)680 base::Time IndexedDBContextImpl::GetOriginLastModified(const Origin& origin) {
681   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
682   if (!HasOrigin(origin))
683     return base::Time();
684 
685   if (is_incognito()) {
686     if (!indexeddb_factory_)
687       return base::Time();
688     return indexeddb_factory_->GetLastModified(origin);
689   }
690 
691   base::FilePath idb_directory = GetLevelDBPath(origin);
692   base::File::Info file_info;
693   if (!base::GetFileInfo(idb_directory, &file_info))
694     return base::Time();
695   return file_info.last_modified;
696 }
697 
GetConnectionCountSync(const Origin & origin)698 size_t IndexedDBContextImpl::GetConnectionCountSync(const Origin& origin) {
699   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
700   if (!HasOrigin(origin))
701     return 0;
702 
703   if (!indexeddb_factory_.get())
704     return 0;
705 
706   return indexeddb_factory_->GetConnectionCount(origin);
707 }
708 
GetStoragePaths(const Origin & origin) const709 std::vector<base::FilePath> IndexedDBContextImpl::GetStoragePaths(
710     const Origin& origin) const {
711   std::vector<base::FilePath> paths = {GetLevelDBPath(origin),
712                                        GetBlobStorePath(origin)};
713   return paths;
714 }
715 
FactoryOpened(const Origin & origin)716 void IndexedDBContextImpl::FactoryOpened(const Origin& origin) {
717   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
718   if (GetOriginSet()->insert(origin).second) {
719     // A newly created db, notify the quota system.
720     QueryDiskAndUpdateQuotaUsage(origin);
721   } else {
722     EnsureDiskUsageCacheInitialized(origin);
723   }
724 }
725 
ConnectionOpened(const Origin & origin,IndexedDBConnection * connection)726 void IndexedDBContextImpl::ConnectionOpened(const Origin& origin,
727                                             IndexedDBConnection* connection) {
728   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
729   quota_manager_proxy()->NotifyStorageAccessed(
730       origin, blink::mojom::StorageType::kTemporary);
731   if (GetOriginSet()->insert(origin).second) {
732     // A newly created db, notify the quota system.
733     QueryDiskAndUpdateQuotaUsage(origin);
734   } else {
735     EnsureDiskUsageCacheInitialized(origin);
736   }
737 }
738 
ConnectionClosed(const Origin & origin,IndexedDBConnection * connection)739 void IndexedDBContextImpl::ConnectionClosed(const Origin& origin,
740                                             IndexedDBConnection* connection) {
741   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
742   quota_manager_proxy()->NotifyStorageAccessed(
743       origin, blink::mojom::StorageType::kTemporary);
744   if (indexeddb_factory_.get() &&
745       indexeddb_factory_->GetConnectionCount(origin) == 0)
746     QueryDiskAndUpdateQuotaUsage(origin);
747 }
748 
TransactionComplete(const Origin & origin)749 void IndexedDBContextImpl::TransactionComplete(const Origin& origin) {
750   DCHECK(!indexeddb_factory_.get() ||
751          indexeddb_factory_->GetConnectionCount(origin) > 0);
752   QueryDiskAndUpdateQuotaUsage(origin);
753 }
754 
DatabaseDeleted(const Origin & origin)755 void IndexedDBContextImpl::DatabaseDeleted(const Origin& origin) {
756   GetOriginSet()->insert(origin);
757   QueryDiskAndUpdateQuotaUsage(origin);
758 }
759 
BlobFilesCleaned(const url::Origin & origin)760 void IndexedDBContextImpl::BlobFilesCleaned(const url::Origin& origin) {
761   QueryDiskAndUpdateQuotaUsage(origin);
762 }
763 
NotifyIndexedDBListChanged(const Origin & origin)764 void IndexedDBContextImpl::NotifyIndexedDBListChanged(const Origin& origin) {
765   for (auto& observer : observers_)
766     observer->OnIndexedDBListChanged(origin);
767 }
768 
NotifyIndexedDBContentChanged(const Origin & origin,const base::string16 & database_name,const base::string16 & object_store_name)769 void IndexedDBContextImpl::NotifyIndexedDBContentChanged(
770     const Origin& origin,
771     const base::string16& database_name,
772     const base::string16& object_store_name) {
773   for (auto& observer : observers_) {
774     observer->OnIndexedDBContentChanged(origin, database_name,
775                                         object_store_name);
776   }
777 }
778 
~IndexedDBContextImpl()779 IndexedDBContextImpl::~IndexedDBContextImpl() {
780   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
781   if (indexeddb_factory_.get())
782     indexeddb_factory_->ContextDestroyed();
783 }
784 
Shutdown()785 void IndexedDBContextImpl::Shutdown() {
786   // Important: This function is NOT called on the IDB Task Runner. All variable
787   // access must be thread-safe.
788   if (is_incognito())
789     return;
790 
791   // TODO(dmurph): Make this variable atomic.
792   if (force_keep_session_state_)
793     return;
794 
795   // Clear session-only databases.
796   if (special_storage_policy_ &&
797       special_storage_policy_->HasSessionOnlyOrigins()) {
798     IDBTaskRunner()->PostTask(
799         FROM_HERE,
800         base::BindOnce(
801             [](const base::FilePath& indexeddb_path,
802                scoped_refptr<IndexedDBContextImpl> context,
803                scoped_refptr<storage::SpecialStoragePolicy>
804                    special_storage_policy) {
805               std::vector<Origin> origins;
806               std::vector<base::FilePath> file_paths;
807               // This function only needs the factory, and not the context, but
808               // the context is used because passing that is thread-safe.
809               IndexedDBFactoryImpl* factory = context->GetIDBFactory();
810               GetAllOriginsAndPaths(indexeddb_path, &origins, &file_paths);
811               DCHECK_EQ(origins.size(), file_paths.size());
812               auto file_path = file_paths.cbegin();
813               auto origin = origins.cbegin();
814               for (; origin != origins.cend(); ++origin, ++file_path) {
815                 const GURL origin_url = GURL(origin->Serialize());
816                 if (!special_storage_policy->IsStorageSessionOnly(origin_url))
817                   continue;
818                 if (special_storage_policy->IsStorageProtected(origin_url))
819                   continue;
820                 if (factory)
821                   factory->ForceClose(*origin, false);
822                 base::DeleteFileRecursively(*file_path);
823               }
824             },
825             data_path_, base::WrapRefCounted(this), special_storage_policy_));
826   }
827 }
828 
GetBlobStorePath(const Origin & origin) const829 base::FilePath IndexedDBContextImpl::GetBlobStorePath(
830     const Origin& origin) const {
831   DCHECK(!is_incognito());
832   return data_path_.Append(indexed_db::GetBlobStoreFileName(origin));
833 }
834 
GetLevelDBPath(const Origin & origin) const835 base::FilePath IndexedDBContextImpl::GetLevelDBPath(
836     const Origin& origin) const {
837   DCHECK(!is_incognito());
838   return data_path_.Append(indexed_db::GetLevelDBFileName(origin));
839 }
840 
ReadUsageFromDisk(const Origin & origin) const841 int64_t IndexedDBContextImpl::ReadUsageFromDisk(const Origin& origin) const {
842   if (is_incognito()) {
843     if (!indexeddb_factory_)
844       return 0;
845     return indexeddb_factory_->GetInMemoryDBSize(origin);
846   }
847 
848   int64_t total_size = 0;
849   for (const base::FilePath& path : GetStoragePaths(origin))
850     total_size += base::ComputeDirectorySize(path);
851   return total_size;
852 }
853 
EnsureDiskUsageCacheInitialized(const Origin & origin)854 void IndexedDBContextImpl::EnsureDiskUsageCacheInitialized(
855     const Origin& origin) {
856   if (origin_size_map_.find(origin) == origin_size_map_.end())
857     origin_size_map_[origin] = ReadUsageFromDisk(origin);
858 }
859 
QueryDiskAndUpdateQuotaUsage(const Origin & origin)860 void IndexedDBContextImpl::QueryDiskAndUpdateQuotaUsage(const Origin& origin) {
861   int64_t former_disk_usage = origin_size_map_[origin];
862   int64_t current_disk_usage = ReadUsageFromDisk(origin);
863   int64_t difference = current_disk_usage - former_disk_usage;
864   if (difference) {
865     origin_size_map_[origin] = current_disk_usage;
866     quota_manager_proxy()->NotifyStorageModified(
867         storage::QuotaClient::kIndexedDatabase, origin,
868         blink::mojom::StorageType::kTemporary, difference);
869     NotifyIndexedDBListChanged(origin);
870   }
871 }
872 
GetOriginSet()873 std::set<Origin>* IndexedDBContextImpl::GetOriginSet() {
874   if (!origin_set_) {
875     std::vector<Origin> origins;
876     GetAllOriginsAndPaths(data_path_, &origins, nullptr);
877     origin_set_ =
878         std::make_unique<std::set<Origin>>(origins.begin(), origins.end());
879   }
880   return origin_set_.get();
881 }
882 
IDBTaskRunner()883 base::SequencedTaskRunner* IndexedDBContextImpl::IDBTaskRunner() {
884   DCHECK(owning_task_runner());
885   return owning_task_runner();
886 }
887 
888 }  // namespace content
889