1 // Copyright (c) 2013 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_factory_impl.h"
6 
7 #include <inttypes.h>
8 #include <stdint.h>
9 
10 #include <string>
11 #include <tuple>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/bind.h"
16 #include "base/callback_helpers.h"
17 #include "base/command_line.h"
18 #include "base/compiler_specific.h"
19 #include "base/feature_list.h"
20 #include "base/files/file_path.h"
21 #include "base/files/file_util.h"
22 #include "base/logging.h"
23 #include "base/metrics/histogram_functions.h"
24 #include "base/metrics/histogram_macros.h"
25 #include "base/sequenced_task_runner.h"
26 #include "base/stl_util.h"
27 #include "base/strings/strcat.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/strings/utf_string_conversions.h"
31 #include "base/system/sys_info.h"
32 #include "base/timer/elapsed_timer.h"
33 #include "base/timer/timer.h"
34 #include "base/trace_event/memory_dump_manager.h"
35 #include "base/trace_event/process_memory_dump.h"
36 #include "components/services/storage/indexed_db/leveldb/leveldb_factory.h"
37 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
38 #include "components/services/storage/indexed_db/scopes/leveldb_scopes_factory.h"
39 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
40 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
41 #include "components/services/storage/public/mojom/indexed_db_control.mojom.h"
42 #include "content/browser/indexed_db/indexed_db_class_factory.h"
43 #include "content/browser/indexed_db/indexed_db_connection.h"
44 #include "content/browser/indexed_db/indexed_db_context_impl.h"
45 #include "content/browser/indexed_db/indexed_db_data_format_version.h"
46 #include "content/browser/indexed_db/indexed_db_database_error.h"
47 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
48 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
49 #include "content/browser/indexed_db/indexed_db_origin_state.h"
50 #include "content/browser/indexed_db/indexed_db_pre_close_task_queue.h"
51 #include "content/browser/indexed_db/indexed_db_reporting.h"
52 #include "content/browser/indexed_db/indexed_db_task_helper.h"
53 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
54 #include "content/browser/indexed_db/indexed_db_tracing.h"
55 #include "storage/browser/blob/mojom/blob_storage_context.mojom.h"
56 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
57 #include "third_party/leveldatabase/env_chromium.h"
58 
59 using base::ASCIIToUTF16;
60 using url::Origin;
61 
62 namespace content {
63 
64 namespace {
65 constexpr static const int kNumOpenTries = 2;
66 
GetDBSizeFromEnv(leveldb::Env * env,const std::string & path,int64_t * total_size_out)67 leveldb::Status GetDBSizeFromEnv(leveldb::Env* env,
68                                  const std::string& path,
69                                  int64_t* total_size_out) {
70   *total_size_out = 0;
71   // Root path should be /, but in MemEnv, a path name is not tailed with '/'
72   DCHECK_EQ(path.back(), '/');
73   const std::string path_without_slash = path.substr(0, path.length() - 1);
74 
75   // This assumes that leveldb will not put a subdirectory into the directory
76   std::vector<std::string> file_names;
77   leveldb::Status s = env->GetChildren(path_without_slash, &file_names);
78   if (!s.ok())
79     return s;
80 
81   for (std::string& file_name : file_names) {
82     file_name.insert(0, path);
83     uint64_t file_size;
84     s = env->GetFileSize(file_name, &file_size);
85     if (!s.ok())
86       return s;
87     else
88       *total_size_out += static_cast<int64_t>(file_size);
89   }
90   return s;
91 }
92 
CreateDefaultError()93 IndexedDBDatabaseError CreateDefaultError() {
94   return IndexedDBDatabaseError(
95       blink::mojom::IDBException::kUnknownError,
96       ASCIIToUTF16("Internal error opening backing store"
97                    " for indexedDB.open."));
98 }
99 
100 // Creates the leveldb and blob storage directories for IndexedDB.
101 std::tuple<base::FilePath /*leveldb_path*/,
102            base::FilePath /*blob_path*/,
103            leveldb::Status>
CreateDatabaseDirectories(const base::FilePath & path_base,const url::Origin & origin)104 CreateDatabaseDirectories(const base::FilePath& path_base,
105                           const url::Origin& origin) {
106   leveldb::Status status;
107   if (!base::CreateDirectoryAndGetError(path_base, nullptr)) {
108     status =
109         leveldb::Status::IOError("Unable to create IndexedDB database path");
110     LOG(ERROR) << status.ToString() << ": \"" << path_base.AsUTF8Unsafe()
111                << "\"";
112     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
113                      origin);
114     return std::make_tuple(base::FilePath(), base::FilePath(), status);
115   }
116 
117   base::FilePath leveldb_path =
118       path_base.Append(indexed_db::GetLevelDBFileName(origin));
119   base::FilePath blob_path =
120       path_base.Append(indexed_db::GetBlobStoreFileName(origin));
121   if (indexed_db::IsPathTooLong(leveldb_path)) {
122     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
123                      origin);
124     status = leveldb::Status::IOError("File path too long");
125     return std::make_tuple(base::FilePath(), base::FilePath(), status);
126   }
127   return std::make_tuple(leveldb_path, blob_path, status);
128 }
129 
AreSchemasKnown(TransactionalLevelDBDatabase * db)130 std::tuple<bool, leveldb::Status> AreSchemasKnown(
131     TransactionalLevelDBDatabase* db) {
132   int64_t db_schema_version = 0;
133   bool found = false;
134   leveldb::Status s = indexed_db::GetInt(db, SchemaVersionKey::Encode(),
135                                          &db_schema_version, &found);
136   if (!s.ok())
137     return std::make_tuple(false, s);
138   if (!found) {
139     return std::make_tuple(true, s);
140   }
141   if (db_schema_version < 0)
142     return std::make_tuple(false, leveldb::Status::Corruption(
143                        "Invalid IndexedDB database schema version."));
144   if (db_schema_version > indexed_db::kLatestKnownSchemaVersion) {
145     return std::make_tuple(false, s);
146   }
147 
148   int64_t raw_db_data_version = 0;
149   s = indexed_db::GetInt(db, DataVersionKey::Encode(), &raw_db_data_version,
150                          &found);
151   if (!s.ok())
152     return std::make_tuple(false, s);
153   if (!found) {
154     return std::make_tuple(true, s);
155   }
156   if (raw_db_data_version < 0)
157     return std::make_tuple(false,
158             leveldb::Status::Corruption("Invalid IndexedDB data version."));
159 
160   return std::make_tuple(IndexedDBDataFormatVersion::GetCurrent().IsAtLeast(
161               IndexedDBDataFormatVersion::Decode(raw_db_data_version)),
162           s);
163 }
164 }  // namespace
165 
IndexedDBFactoryImpl(IndexedDBContextImpl * context,IndexedDBClassFactory * indexed_db_class_factory,base::Clock * clock)166 IndexedDBFactoryImpl::IndexedDBFactoryImpl(
167     IndexedDBContextImpl* context,
168     IndexedDBClassFactory* indexed_db_class_factory,
169     base::Clock* clock)
170     : context_(context),
171       class_factory_(indexed_db_class_factory),
172       clock_(clock) {
173   DCHECK(context);
174   DCHECK(indexed_db_class_factory);
175   DCHECK(clock);
176   base::trace_event::MemoryDumpManager::GetInstance()
177       ->RegisterDumpProviderWithSequencedTaskRunner(
178           this, "IndexedDBFactoryImpl", base::SequencedTaskRunnerHandle::Get(),
179           base::trace_event::MemoryDumpProvider::Options());
180 }
181 
~IndexedDBFactoryImpl()182 IndexedDBFactoryImpl::~IndexedDBFactoryImpl() {
183   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
185       this);
186 }
187 
GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,const Origin & origin,const base::FilePath & data_directory)188 void IndexedDBFactoryImpl::GetDatabaseInfo(
189     scoped_refptr<IndexedDBCallbacks> callbacks,
190     const Origin& origin,
191     const base::FilePath& data_directory) {
192   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193   IDB_TRACE("IndexedDBFactoryImpl::GetDatabaseInfo");
194   IndexedDBOriginStateHandle origin_state_handle;
195   leveldb::Status s;
196   IndexedDBDatabaseError error;
197   // Note: Any data loss information here is not piped up to the renderer, and
198   // will be lost.
199   std::tie(origin_state_handle, s, error, std::ignore, std::ignore) =
200       GetOrOpenOriginFactory(origin, data_directory,
201                              /*create_if_missing=*/true);
202   if (!origin_state_handle.IsHeld() || !origin_state_handle.origin_state()) {
203     callbacks->OnError(error);
204     if (s.IsCorruption())
205       HandleBackingStoreCorruption(origin, error);
206     return;
207   }
208   IndexedDBOriginState* factory = origin_state_handle.origin_state();
209 
210   IndexedDBMetadataCoding metadata_coding;
211   std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions;
212   s = metadata_coding.ReadDatabaseNamesAndVersions(
213       factory->backing_store_->db(),
214       factory->backing_store_->origin_identifier(), &names_and_versions);
215   if (!s.ok()) {
216     error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
217                                    "Internal error opening backing store for "
218                                    "indexedDB.databases().");
219     callbacks->OnError(error);
220     if (s.IsCorruption())
221       HandleBackingStoreCorruption(origin, error);
222     return;
223   }
224   callbacks->OnSuccess(std::move(names_and_versions));
225 }
226 
GetDatabaseNames(scoped_refptr<IndexedDBCallbacks> callbacks,const Origin & origin,const base::FilePath & data_directory)227 void IndexedDBFactoryImpl::GetDatabaseNames(
228     scoped_refptr<IndexedDBCallbacks> callbacks,
229     const Origin& origin,
230     const base::FilePath& data_directory) {
231   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
232   IDB_TRACE("IndexedDBFactoryImpl::GetDatabaseInfo");
233   IndexedDBOriginStateHandle origin_state_handle;
234   leveldb::Status s;
235   IndexedDBDatabaseError error;
236   // Note: Any data loss information here is not piped up to the renderer, and
237   // will be lost.
238   std::tie(origin_state_handle, s, error, std::ignore, std::ignore) =
239       GetOrOpenOriginFactory(origin, data_directory,
240                              /*create_if_missing=*/false);
241   if (!origin_state_handle.IsHeld() || !origin_state_handle.origin_state()) {
242     if (s.IsNotFound()) {
243       callbacks->OnSuccess(std::vector<base::string16>());
244     } else {
245       callbacks->OnError(error);
246     }
247     if (s.IsCorruption())
248       HandleBackingStoreCorruption(origin, error);
249     return;
250   }
251   IndexedDBOriginState* factory = origin_state_handle.origin_state();
252 
253   IndexedDBMetadataCoding metadata_coding;
254   std::vector<base::string16> names;
255   s = metadata_coding.ReadDatabaseNames(
256       factory->backing_store_->db(),
257       factory->backing_store_->origin_identifier(), &names);
258   if (!s.ok()) {
259     error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
260                                    "Internal error opening backing store for "
261                                    "indexedDB.webkitGetDatabaseNames.");
262     callbacks->OnError(error);
263     if (s.IsCorruption())
264       HandleBackingStoreCorruption(origin, error);
265     return;
266   }
267   callbacks->OnSuccess(names);
268 }
269 
Open(const base::string16 & name,std::unique_ptr<IndexedDBPendingConnection> connection,const Origin & origin,const base::FilePath & data_directory)270 void IndexedDBFactoryImpl::Open(
271     const base::string16& name,
272     std::unique_ptr<IndexedDBPendingConnection> connection,
273     const Origin& origin,
274     const base::FilePath& data_directory) {
275   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
276   IDB_TRACE("IndexedDBFactoryImpl::Open");
277   IndexedDBDatabase::Identifier unique_identifier(origin, name);
278   IndexedDBOriginStateHandle origin_state_handle;
279   leveldb::Status s;
280   IndexedDBDatabaseError error;
281   std::tie(origin_state_handle, s, error, connection->data_loss_info,
282            connection->was_cold_open) =
283       GetOrOpenOriginFactory(origin, data_directory,
284                              /*create_if_missing=*/true);
285   if (!origin_state_handle.IsHeld() || !origin_state_handle.origin_state()) {
286     connection->callbacks->OnError(error);
287     if (s.IsCorruption())
288       HandleBackingStoreCorruption(origin, error);
289     return;
290   }
291   IndexedDBOriginState* factory = origin_state_handle.origin_state();
292   auto it = factory->databases().find(name);
293   if (it != factory->databases().end()) {
294     it->second->ScheduleOpenConnection(std::move(origin_state_handle),
295                                        std::move(connection));
296     return;
297   }
298   std::unique_ptr<IndexedDBDatabase> database;
299   std::tie(database, s) = class_factory_->CreateIndexedDBDatabase(
300       name, factory->backing_store(), this,
301       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForOrigin,
302                           origin_state_destruction_weak_factory_.GetWeakPtr(),
303                           origin),
304       std::make_unique<IndexedDBMetadataCoding>(), std::move(unique_identifier),
305       factory->lock_manager());
306   if (!database.get()) {
307     error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
308                                    ASCIIToUTF16("Internal error creating "
309                                                 "database backend for "
310                                                 "indexedDB.open."));
311     connection->callbacks->OnError(error);
312     if (s.IsCorruption())
313       HandleBackingStoreCorruption(origin, error);
314     return;
315   }
316 
317   // The database must be added before the schedule call, as the
318   // CreateDatabaseDeleteClosure can be called synchronously.
319   auto* database_ptr = database.get();
320   factory->AddDatabase(name, std::move(database));
321   database_ptr->ScheduleOpenConnection(std::move(origin_state_handle),
322                                        std::move(connection));
323 }
324 
DeleteDatabase(const base::string16 & name,scoped_refptr<IndexedDBCallbacks> callbacks,const Origin & origin,const base::FilePath & data_directory,bool force_close)325 void IndexedDBFactoryImpl::DeleteDatabase(
326     const base::string16& name,
327     scoped_refptr<IndexedDBCallbacks> callbacks,
328     const Origin& origin,
329     const base::FilePath& data_directory,
330     bool force_close) {
331   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
332   IDB_TRACE("IndexedDBFactoryImpl::DeleteDatabase");
333   IndexedDBDatabase::Identifier unique_identifier(origin, name);
334   IndexedDBOriginStateHandle origin_state_handle;
335   leveldb::Status s;
336   IndexedDBDatabaseError error;
337   // Note: Any data loss information here is not piped up to the renderer, and
338   // will be lost.
339   std::tie(origin_state_handle, s, error, std::ignore, std::ignore) =
340       GetOrOpenOriginFactory(origin, data_directory,
341                              /*create_if_missing=*/true);
342   if (!origin_state_handle.IsHeld() || !origin_state_handle.origin_state()) {
343     callbacks->OnError(error);
344     if (s.IsCorruption())
345       HandleBackingStoreCorruption(origin, error);
346     return;
347   }
348   IndexedDBOriginState* factory = origin_state_handle.origin_state();
349 
350   auto it = factory->databases().find(name);
351   if (it != factory->databases().end()) {
352     base::WeakPtr<IndexedDBDatabase> database = it->second->AsWeakPtr();
353     database->ScheduleDeleteDatabase(
354         std::move(origin_state_handle), callbacks,
355         base::BindOnce(&IndexedDBFactoryImpl::OnDatabaseDeleted,
356                        weak_factory_.GetWeakPtr(), origin));
357     if (force_close) {
358       leveldb::Status status = database->ForceCloseAndRunTasks();
359       if (!status.ok())
360         OnDatabaseError(origin, status, "Error aborting transactions.");
361     }
362     return;
363   }
364 
365   // TODO(dmurph): Get rid of on-demand metadata loading, and store metadata
366   // in-memory in the backing store.
367   IndexedDBMetadataCoding metadata_coding;
368   std::vector<base::string16> names;
369   s = metadata_coding.ReadDatabaseNames(
370       factory->backing_store()->db(),
371       factory->backing_store()->origin_identifier(), &names);
372   if (!s.ok()) {
373     error = IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
374                                    "Internal error opening backing store for "
375                                    "indexedDB.deleteDatabase.");
376     callbacks->OnError(error);
377     if (s.IsCorruption())
378       HandleBackingStoreCorruption(origin, error);
379     return;
380   }
381 
382   if (!base::Contains(names, name)) {
383     const int64_t version = 0;
384     callbacks->OnSuccess(version);
385     return;
386   }
387 
388   std::unique_ptr<IndexedDBDatabase> database;
389   std::tie(database, s) = class_factory_->CreateIndexedDBDatabase(
390       name, factory->backing_store(), this,
391       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForOrigin,
392                           origin_state_destruction_weak_factory_.GetWeakPtr(),
393                           origin),
394       std::make_unique<IndexedDBMetadataCoding>(), unique_identifier,
395       factory->lock_manager());
396   if (!database.get()) {
397     error = IndexedDBDatabaseError(
398         blink::mojom::IDBException::kUnknownError,
399         ASCIIToUTF16("Internal error creating database backend for "
400                      "indexedDB.deleteDatabase."));
401     callbacks->OnError(error);
402     if (s.IsCorruption())
403       HandleBackingStoreCorruption(origin, error);
404     return;
405   }
406 
407   base::WeakPtr<IndexedDBDatabase> database_ptr =
408       factory->AddDatabase(name, std::move(database))->AsWeakPtr();
409   database_ptr->ScheduleDeleteDatabase(
410       std::move(origin_state_handle), std::move(callbacks),
411       base::BindOnce(&IndexedDBFactoryImpl::OnDatabaseDeleted,
412                      weak_factory_.GetWeakPtr(), origin));
413   if (force_close) {
414     leveldb::Status status = database_ptr->ForceCloseAndRunTasks();
415     if (!status.ok())
416       OnDatabaseError(origin, status, "Error aborting transactions.");
417   }
418 }
419 
AbortTransactionsAndCompactDatabase(base::OnceCallback<void (leveldb::Status)> callback,const Origin & origin)420 void IndexedDBFactoryImpl::AbortTransactionsAndCompactDatabase(
421     base::OnceCallback<void(leveldb::Status)> callback,
422     const Origin& origin) {
423   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
424   IDB_TRACE("IndexedDBFactoryImpl::AbortTransactionsAndCompactDatabase");
425   auto it = factories_per_origin_.find(origin);
426   if (it == factories_per_origin_.end()) {
427     std::move(callback).Run(leveldb::Status::OK());
428     return;
429   }
430   it->second->AbortAllTransactions(true);
431   RunTasksForOrigin(it->second->AsWeakPtr());
432   std::move(callback).Run(leveldb::Status::OK());
433 }
434 
AbortTransactionsForDatabase(base::OnceCallback<void (leveldb::Status)> callback,const Origin & origin)435 void IndexedDBFactoryImpl::AbortTransactionsForDatabase(
436     base::OnceCallback<void(leveldb::Status)> callback,
437     const Origin& origin) {
438   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
439   IDB_TRACE("IndexedDBFactoryImpl::AbortTransactionsForDatabase");
440   auto it = factories_per_origin_.find(origin);
441   if (it == factories_per_origin_.end()) {
442     std::move(callback).Run(leveldb::Status::OK());
443     return;
444   }
445   it->second->AbortAllTransactions(false);
446   RunTasksForOrigin(it->second->AsWeakPtr());
447   std::move(callback).Run(leveldb::Status::OK());
448 }
449 
HandleBackingStoreFailure(const Origin & origin)450 void IndexedDBFactoryImpl::HandleBackingStoreFailure(const Origin& origin) {
451   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
452   // nullptr after ContextDestroyed() called, and in some unit tests.
453   if (!context_)
454     return;
455   context_->ForceCloseSync(
456       origin,
457       storage::mojom::ForceCloseReason::FORCE_CLOSE_BACKING_STORE_FAILURE);
458 }
459 
HandleBackingStoreCorruption(const Origin & origin,const IndexedDBDatabaseError & error)460 void IndexedDBFactoryImpl::HandleBackingStoreCorruption(
461     const Origin& origin,
462     const IndexedDBDatabaseError& error) {
463   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
464   // Make a copy of origin as this is likely a reference to a member of a
465   // backing store which this function will be deleting.
466   Origin saved_origin(origin);
467   DCHECK(context_);
468   base::FilePath path_base = context_->data_path();
469 
470   // The message may contain the database path, which may be considered
471   // sensitive data, and those strings are passed to the extension, so strip it.
472   std::string sanitized_message = base::UTF16ToUTF8(error.message());
473   base::ReplaceSubstringsAfterOffset(&sanitized_message, 0u,
474                                      path_base.AsUTF8Unsafe(), "...");
475   IndexedDBBackingStore::RecordCorruptionInfo(path_base, saved_origin,
476                                               sanitized_message);
477   HandleBackingStoreFailure(saved_origin);
478   // Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
479   //       so our corruption info file will remain.
480   const base::FilePath file_path =
481       path_base.Append(indexed_db::GetLevelDBFileName(saved_origin));
482   leveldb::Status s =
483       class_factory_->leveldb_factory().DestroyLevelDB(file_path);
484   DLOG_IF(ERROR, !s.ok()) << "Unable to delete backing store: " << s.ToString();
485   base::UmaHistogramEnumeration(
486       "WebCore.IndexedDB.DestroyCorruptBackingStoreStatus",
487       leveldb_env::GetLevelDBStatusUMAValue(s),
488       leveldb_env::LEVELDB_STATUS_MAX);
489 }
490 
GetOpenDatabasesForOrigin(const Origin & origin) const491 std::vector<IndexedDBDatabase*> IndexedDBFactoryImpl::GetOpenDatabasesForOrigin(
492     const Origin& origin) const {
493   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
494   auto it = factories_per_origin_.find(origin);
495   if (it == factories_per_origin_.end()) {
496     return std::vector<IndexedDBDatabase*>();
497   }
498   IndexedDBOriginState* factory = it->second.get();
499   std::vector<IndexedDBDatabase*> out;
500   out.reserve(factory->databases().size());
501   std::for_each(factory->databases().begin(), factory->databases().end(),
502                 [&out](const auto& p) { out.push_back(p.second.get()); });
503   return out;
504 }
505 
ForceClose(const Origin & origin,bool delete_in_memory_store)506 void IndexedDBFactoryImpl::ForceClose(const Origin& origin,
507                                       bool delete_in_memory_store) {
508   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
509   auto it = factories_per_origin_.find(origin);
510   if (it == factories_per_origin_.end())
511     return;
512 
513   base::WeakPtr<IndexedDBOriginState> weak_ptr;
514   {
515     IndexedDBOriginStateHandle origin_state_handle = it->second->CreateHandle();
516 
517     if (delete_in_memory_store)
518       origin_state_handle.origin_state()->StopPersistingForIncognito();
519     origin_state_handle.origin_state()->ForceClose();
520     weak_ptr = origin_state_handle.origin_state()->AsWeakPtr();
521   }
522   // Run tasks so the origin state is deleted.
523   RunTasksForOrigin(std::move(weak_ptr));
524 }
525 
ForceSchemaDowngrade(const Origin & origin)526 void IndexedDBFactoryImpl::ForceSchemaDowngrade(const Origin& origin) {
527   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
528   auto it = factories_per_origin_.find(origin);
529   if (it == factories_per_origin_.end())
530     return;
531 
532   IndexedDBBackingStore* backing_store = it->second->backing_store();
533   leveldb::Status s = backing_store->RevertSchemaToV2();
534   DLOG_IF(ERROR, !s.ok()) << "Unable to force downgrade: " << s.ToString();
535 }
536 
HasV2SchemaCorruption(const Origin & origin)537 V2SchemaCorruptionStatus IndexedDBFactoryImpl::HasV2SchemaCorruption(
538     const Origin& origin) {
539   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
540   auto it = factories_per_origin_.find(origin);
541   if (it == factories_per_origin_.end())
542     return V2SchemaCorruptionStatus::kUnknown;
543 
544   IndexedDBBackingStore* backing_store = it->second->backing_store();
545   return backing_store->HasV2SchemaCorruption();
546 }
547 
ContextDestroyed()548 void IndexedDBFactoryImpl::ContextDestroyed() {
549   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
550   // Set |context_| to nullptr first to ensure no re-entry into the |cotext_|
551   // object during shutdown. This can happen in methods like BlobFilesCleaned.
552   context_ = nullptr;
553   // Invalidate the weak factory that is used by the IndexedDBOriginStates to
554   // destruct themselves. This prevents modification of the
555   // |factories_per_origin_| map while it is iterated below, and allows us to
556   // avoid holding a handle to call ForceClose();
557   origin_state_destruction_weak_factory_.InvalidateWeakPtrs();
558   for (const auto& pair : factories_per_origin_) {
559     pair.second->ForceClose();
560   }
561   factories_per_origin_.clear();
562 }
563 
ReportOutstandingBlobs(const Origin & origin,bool blobs_outstanding)564 void IndexedDBFactoryImpl::ReportOutstandingBlobs(const Origin& origin,
565                                                   bool blobs_outstanding) {
566   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
567   if (!context_)
568     return;
569   auto it = factories_per_origin_.find(origin);
570   DCHECK(it != factories_per_origin_.end());
571 
572   it->second->ReportOutstandingBlobs(blobs_outstanding);
573 }
574 
BlobFilesCleaned(const url::Origin & origin)575 void IndexedDBFactoryImpl::BlobFilesCleaned(const url::Origin& origin) {
576   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
577   // nullptr after ContextDestroyed() called, and in some unit tests.
578   if (!context_)
579     return;
580   context_->BlobFilesCleaned(origin);
581 }
582 
GetConnectionCount(const Origin & origin) const583 size_t IndexedDBFactoryImpl::GetConnectionCount(const Origin& origin) const {
584   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
585   auto it = factories_per_origin_.find(origin);
586   if (it == factories_per_origin_.end())
587     return 0;
588   size_t count = 0;
589   for (const auto& name_database_pair : it->second->databases()) {
590     count += name_database_pair.second->ConnectionCount();
591   }
592 
593   return count;
594 }
595 
NotifyIndexedDBContentChanged(const url::Origin & origin,const base::string16 & database_name,const base::string16 & object_store_name)596 void IndexedDBFactoryImpl::NotifyIndexedDBContentChanged(
597     const url::Origin& origin,
598     const base::string16& database_name,
599     const base::string16& object_store_name) {
600   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
601   if (!context_)
602     return;
603   context_->NotifyIndexedDBContentChanged(origin, database_name,
604                                           object_store_name);
605 }
606 
GetInMemoryDBSize(const Origin & origin) const607 int64_t IndexedDBFactoryImpl::GetInMemoryDBSize(const Origin& origin) const {
608   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
609   auto it = factories_per_origin_.find(origin);
610   if (it == factories_per_origin_.end())
611     return 0;
612   IndexedDBBackingStore* backing_store = it->second->backing_store();
613   int64_t level_db_size = 0;
614   leveldb::Status s =
615       GetDBSizeFromEnv(backing_store->db()->env(), "/", &level_db_size);
616   if (!s.ok())
617     LOG(ERROR) << "Failed to GetDBSizeFromEnv: " << s.ToString();
618 
619   return backing_store->GetInMemoryBlobSize() + level_db_size;
620 }
621 
GetLastModified(const url::Origin & origin) const622 base::Time IndexedDBFactoryImpl::GetLastModified(
623     const url::Origin& origin) const {
624   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
625   auto it = factories_per_origin_.find(origin);
626   if (it == factories_per_origin_.end())
627     return base::Time();
628   IndexedDBBackingStore* backing_store = it->second->backing_store();
629   return backing_store->db()->LastModified();
630 }
631 
GetOpenOrigins() const632 std::vector<url::Origin> IndexedDBFactoryImpl::GetOpenOrigins() const {
633   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
634   std::vector<url::Origin> output;
635   for (const auto& pair : factories_per_origin_) {
636     output.push_back(pair.first);
637   }
638   return output;
639 }
640 
GetOriginFactory(const url::Origin & origin) const641 IndexedDBOriginState* IndexedDBFactoryImpl::GetOriginFactory(
642     const url::Origin& origin) const {
643   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
644   auto it = factories_per_origin_.find(origin);
645   if (it != factories_per_origin_.end())
646     return it->second.get();
647   return nullptr;
648 }
649 
650 std::tuple<IndexedDBOriginStateHandle,
651            leveldb::Status,
652            IndexedDBDatabaseError,
653            IndexedDBDataLossInfo,
654            /*is_cold_open=*/bool>
GetOrOpenOriginFactory(const Origin & origin,const base::FilePath & data_directory,bool create_if_missing)655 IndexedDBFactoryImpl::GetOrOpenOriginFactory(
656     const Origin& origin,
657     const base::FilePath& data_directory,
658     bool create_if_missing) {
659   IDB_TRACE("indexed_db::GetOrOpenOriginFactory");
660   // Please see docs/open_and_verify_leveldb_database.code2flow, and the
661   // generated pdf (from https://code2flow.com).
662   // The intended strategy here is to have this function match that flowchart,
663   // where the flowchart should be seen as the 'master' logic template. Please
664   // check the git history of both to make sure they are in sync.
665   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
666   auto it = factories_per_origin_.find(origin);
667   if (it != factories_per_origin_.end()) {
668     return std::make_tuple(it->second->CreateHandle(), leveldb::Status::OK(),
669                            IndexedDBDatabaseError(), IndexedDBDataLossInfo(),
670                            /*was_cold_open=*/false);
671   }
672   UMA_HISTOGRAM_ENUMERATION(
673       indexed_db::kBackingStoreActionUmaName,
674       indexed_db::IndexedDBAction::kBackingStoreOpenAttempt);
675 
676   bool is_incognito_and_in_memory = data_directory.empty();
677   base::FilePath blob_path;
678   base::FilePath database_path;
679   leveldb::Status s = leveldb::Status::OK();
680   if (!is_incognito_and_in_memory) {
681     // The database will be on-disk and not in-memory.
682     std::tie(database_path, blob_path, s) =
683         CreateDatabaseDirectories(data_directory, origin);
684     if (!s.ok())
685       return std::make_tuple(IndexedDBOriginStateHandle(), s, CreateDefaultError(),
686                              IndexedDBDataLossInfo(), /*was_cold_open=*/true);
687   }
688 
689   // TODO(dmurph) Have these factories be given in the constructor, or as
690   // arguments to this method.
691   DefaultLevelDBScopesFactory scopes_factory;
692   std::unique_ptr<DisjointRangeLockManager> lock_manager =
693       std::make_unique<DisjointRangeLockManager>(kIndexedDBLockLevelCount);
694   IndexedDBDataLossInfo data_loss_info;
695   std::unique_ptr<IndexedDBBackingStore> backing_store;
696   bool disk_full = false;
697   base::ElapsedTimer open_timer;
698   leveldb::Status first_try_status;
699   for (int i = 0; i < kNumOpenTries; ++i) {
700     LevelDBScopesOptions scopes_options;
701     scopes_options.lock_manager = lock_manager.get();
702     scopes_options.metadata_key_prefix = ScopesPrefix::Encode();
703     scopes_options.failure_callback = base::BindRepeating(
704         [](const Origin& origin, base::WeakPtr<IndexedDBFactoryImpl> factory,
705            leveldb::Status s) {
706           if (!factory)
707             return;
708           factory->OnDatabaseError(origin, s, nullptr);
709         },
710         origin, weak_factory_.GetWeakPtr());
711     const bool is_first_attempt = i == 0;
712     std::tie(backing_store, s, data_loss_info, disk_full) =
713         OpenAndVerifyIndexedDBBackingStore(
714             origin, data_directory, database_path, blob_path,
715             std::move(scopes_options), &scopes_factory, is_first_attempt,
716             create_if_missing);
717     if (LIKELY(is_first_attempt))
718       first_try_status = s;
719     if (LIKELY(s.ok()))
720       break;
721     DCHECK(!backing_store);
722     // If the disk is full, always exit immediately.
723     if (disk_full)
724       break;
725     if (s.IsCorruption()) {
726       std::string sanitized_message = leveldb_env::GetCorruptionMessage(s);
727       base::ReplaceSubstringsAfterOffset(&sanitized_message, 0u,
728                                          data_directory.AsUTF8Unsafe(), "...");
729       LOG(ERROR) << "Got corruption for " << origin.Serialize() << ", "
730                  << sanitized_message;
731       IndexedDBBackingStore::RecordCorruptionInfo(data_directory, origin,
732                                                   sanitized_message);
733     }
734   }
735 
736   UMA_HISTOGRAM_ENUMERATION(
737       "WebCore.IndexedDB.BackingStore.OpenFirstTryResult",
738       leveldb_env::GetLevelDBStatusUMAValue(first_try_status),
739       leveldb_env::LEVELDB_STATUS_MAX);
740 
741   if (LIKELY(first_try_status.ok())) {
742     UMA_HISTOGRAM_TIMES(
743         "WebCore.IndexedDB.BackingStore.OpenFirstTrySuccessTime",
744         open_timer.Elapsed());
745   }
746 
747   if (UNLIKELY(!s.ok())) {
748     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
749                      origin);
750 
751     if (disk_full) {
752       context_->IOTaskRunner()->PostTask(
753           FROM_HERE,
754           base::BindOnce(&storage::QuotaManagerProxy::NotifyWriteFailed,
755                          context_->quota_manager_proxy(), origin));
756       return std::make_tuple(IndexedDBOriginStateHandle(), s,
757               IndexedDBDatabaseError(
758                   blink::mojom::IDBException::kQuotaError,
759                   ASCIIToUTF16("Encountered full disk while opening "
760                                "backing store for indexedDB.open.")),
761               data_loss_info, /*was_cold_open=*/true);
762 
763     } else {
764       return std::make_tuple(IndexedDBOriginStateHandle(), s, CreateDefaultError(),
765               data_loss_info, /*was_cold_open=*/true);
766     }
767   }
768   DCHECK(backing_store);
769   // Scopes must be single sequence to keep methods like ForceClose synchronous.
770   // See https://crbug.com/980685
771   s = backing_store->db()->scopes()->StartRecoveryAndCleanupTasks(
772       LevelDBScopes::TaskRunnerMode::kNewCleanupAndRevertSequences);
773 
774   if (UNLIKELY(!s.ok())) {
775     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
776                      origin);
777 
778     return std::make_tuple(IndexedDBOriginStateHandle(), s, CreateDefaultError(),
779             data_loss_info, /*was_cold_open=*/true);
780   }
781 
782   if (!is_incognito_and_in_memory)
783     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin);
784 
785   auto run_tasks_callback = base::BindRepeating(
786       &IndexedDBFactoryImpl::MaybeRunTasksForOrigin,
787       origin_state_destruction_weak_factory_.GetWeakPtr(), origin);
788 
789   auto tear_down_callback = base::BindRepeating(
790       [](const Origin& origin, base::WeakPtr<IndexedDBFactoryImpl> factory,
791          leveldb::Status s) {
792         if (!factory)
793           return;
794         factory->OnDatabaseError(origin, s, nullptr);
795       },
796       origin, weak_factory_.GetWeakPtr());
797 
798   auto origin_state = std::make_unique<IndexedDBOriginState>(
799       origin,
800       /*persist_for_incognito=*/is_incognito_and_in_memory, clock_,
801       &class_factory_->transactional_leveldb_factory(), &earliest_sweep_,
802       std::move(lock_manager), std::move(run_tasks_callback),
803       std::move(tear_down_callback), std::move(backing_store));
804 
805   it = factories_per_origin_.emplace(origin, std::move(origin_state)).first;
806 
807   context_->FactoryOpened(origin);
808   return std::make_tuple(it->second->CreateHandle(), s, IndexedDBDatabaseError(),
809           data_loss_info, /*was_cold_open=*/true);
810 }
811 
CreateBackingStore(IndexedDBBackingStore::Mode backing_store_mode,TransactionalLevelDBFactory * transactional_leveldb_factory,const url::Origin & origin,const base::FilePath & blob_path,std::unique_ptr<TransactionalLevelDBDatabase> db,storage::mojom::BlobStorageContext * blob_storage_context,storage::mojom::NativeFileSystemContext * native_file_system_context,IndexedDBBackingStore::BlobFilesCleanedCallback blob_files_cleaned,IndexedDBBackingStore::ReportOutstandingBlobsCallback report_outstanding_blobs,scoped_refptr<base::SequencedTaskRunner> idb_task_runner,scoped_refptr<base::SequencedTaskRunner> io_task_runner)812 std::unique_ptr<IndexedDBBackingStore> IndexedDBFactoryImpl::CreateBackingStore(
813     IndexedDBBackingStore::Mode backing_store_mode,
814     TransactionalLevelDBFactory* transactional_leveldb_factory,
815     const url::Origin& origin,
816     const base::FilePath& blob_path,
817     std::unique_ptr<TransactionalLevelDBDatabase> db,
818     storage::mojom::BlobStorageContext* blob_storage_context,
819     storage::mojom::NativeFileSystemContext* native_file_system_context,
820     IndexedDBBackingStore::BlobFilesCleanedCallback blob_files_cleaned,
821     IndexedDBBackingStore::ReportOutstandingBlobsCallback
822         report_outstanding_blobs,
823     scoped_refptr<base::SequencedTaskRunner> idb_task_runner,
824     scoped_refptr<base::SequencedTaskRunner> io_task_runner) {
825   return std::make_unique<IndexedDBBackingStore>(
826       backing_store_mode, transactional_leveldb_factory, origin, blob_path,
827       std::move(db), blob_storage_context, native_file_system_context,
828       std::move(blob_files_cleaned), std::move(report_outstanding_blobs),
829       std::move(idb_task_runner), std::move(io_task_runner));
830 }
831 std::tuple<std::unique_ptr<IndexedDBBackingStore>,
832            leveldb::Status,
833            IndexedDBDataLossInfo,
834            bool /* is_disk_full */>
OpenAndVerifyIndexedDBBackingStore(const url::Origin & origin,base::FilePath data_directory,base::FilePath database_path,base::FilePath blob_path,LevelDBScopesOptions scopes_options,LevelDBScopesFactory * scopes_factory,bool is_first_attempt,bool create_if_missing)835 IndexedDBFactoryImpl::OpenAndVerifyIndexedDBBackingStore(
836     const url::Origin& origin,
837     base::FilePath data_directory,
838     base::FilePath database_path,
839     base::FilePath blob_path,
840     LevelDBScopesOptions scopes_options,
841     LevelDBScopesFactory* scopes_factory,
842     bool is_first_attempt,
843     bool create_if_missing) {
844   // Please see docs/open_and_verify_leveldb_database.code2flow, and the
845   // generated pdf (from https://code2flow.com).
846   // The intended strategy here is to have this function match that flowchart,
847   // where the flowchart should be seen as the 'master' logic template. Please
848   // check the git history of both to make sure they are in sync.
849   DCHECK_EQ(database_path.empty(), data_directory.empty());
850   DCHECK_EQ(blob_path.empty(), data_directory.empty());
851   IDB_TRACE("indexed_db::OpenAndVerifyLevelDBDatabase");
852 
853   bool is_incognito_and_in_memory = data_directory.empty();
854   leveldb::Status status;
855   IndexedDBDataLossInfo data_loss_info;
856   data_loss_info.status = blink::mojom::IDBDataLoss::None;
857   if (!is_incognito_and_in_memory) {
858     // Check for previous corruption, and if found then try to delete the
859     // database.
860     std::string corruption_message =
861         indexed_db::ReadCorruptionInfo(data_directory, origin);
862     if (UNLIKELY(!corruption_message.empty())) {
863       LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
864                     "database.";
865       if (is_first_attempt) {
866         ReportOpenStatus(
867             indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
868             origin);
869       }
870       data_loss_info.status = blink::mojom::IDBDataLoss::Total;
871       data_loss_info.message = base::StrCat(
872           {"IndexedDB (database was corrupt): ", corruption_message});
873       // This is a special case where we want to make sure the database is
874       // deleted, so we try to delete again.
875       status = class_factory_->leveldb_factory().DestroyLevelDB(database_path);
876       base::UmaHistogramEnumeration(
877           "WebCore.IndexedDB.DestroyCorruptBackingStoreStatus",
878           leveldb_env::GetLevelDBStatusUMAValue(status),
879           leveldb_env::LEVELDB_STATUS_MAX);
880       if (UNLIKELY(!status.ok())) {
881         LOG(ERROR) << "Unable to delete backing store: " << status.ToString();
882         return std::make_tuple(nullptr, status, data_loss_info, /*is_disk_full=*/false);
883       }
884     }
885   }
886 
887   // Open the leveldb database.
888   scoped_refptr<LevelDBState> database_state;
889   bool is_disk_full;
890   {
891     IDB_TRACE("IndexedDBFactoryImpl::OpenLevelDB");
892     base::TimeTicks begin_time = base::TimeTicks::Now();
893     size_t write_buffer_size = leveldb_env::WriteBufferSize(
894         base::SysInfo::AmountOfTotalDiskSpace(database_path));
895     std::tie(database_state, status, is_disk_full) =
896         class_factory_->leveldb_factory().OpenLevelDBState(
897             database_path, create_if_missing, write_buffer_size);
898     if (UNLIKELY(!status.ok())) {
899       if (!status.IsNotFound()) {
900         indexed_db::ReportLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors",
901                                        status);
902       }
903       return std::make_tuple(nullptr, status, IndexedDBDataLossInfo(),
904                              is_disk_full);
905     }
906     UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.OpenTime",
907                                base::TimeTicks::Now() - begin_time);
908   }
909 
910   // Create the LevelDBScopes wrapper.
911   std::unique_ptr<LevelDBScopes> scopes;
912   {
913     IDB_TRACE("IndexedDBFactoryImpl::OpenLevelDBScopes");
914     DCHECK(scopes_factory);
915     std::tie(scopes, status) = scopes_factory->CreateAndInitializeLevelDBScopes(
916         std::move(scopes_options), database_state);
917     if (UNLIKELY(!status.ok()))
918       return std::make_tuple(nullptr, status, std::move(data_loss_info),
919                              /*is_disk_full=*/false);
920   }
921 
922   // Create the TransactionalLevelDBDatabase wrapper.
923   std::unique_ptr<TransactionalLevelDBDatabase> database =
924       class_factory_->transactional_leveldb_factory().CreateLevelDBDatabase(
925           std::move(database_state), std::move(scopes),
926           context_->IDBTaskRunner(),
927           TransactionalLevelDBDatabase::kDefaultMaxOpenIteratorsPerDatabase);
928 
929   bool are_schemas_known = false;
930   std::tie(are_schemas_known, status) = AreSchemasKnown(database.get());
931   if (UNLIKELY(!status.ok())) {
932     LOG(ERROR) << "IndexedDB had an error checking schema, treating it as "
933                   "failure to open: "
934                << status.ToString();
935     ReportOpenStatus(
936         indexed_db::
937             INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
938         origin);
939     return std::make_tuple(nullptr, status, std::move(data_loss_info), /*is_disk_full=*/false);
940   } else if (UNLIKELY(!are_schemas_known)) {
941     LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it as "
942                   "failure to open.";
943     ReportOpenStatus(
944         indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
945         origin);
946     return std::make_tuple(nullptr, leveldb::Status::Corruption("Unknown IndexedDB schema"),
947             std::move(data_loss_info), /*is_disk_full=*/false);
948   }
949 
950   bool first_open_since_startup =
951       backends_opened_since_startup_.insert(origin).second;
952   IndexedDBBackingStore::Mode backing_store_mode =
953       is_incognito_and_in_memory ? IndexedDBBackingStore::Mode::kInMemory
954                                  : IndexedDBBackingStore::Mode::kOnDisk;
955   std::unique_ptr<IndexedDBBackingStore> backing_store = CreateBackingStore(
956       backing_store_mode, &class_factory_->transactional_leveldb_factory(),
957       origin, blob_path, std::move(database), context_->blob_storage_context(),
958       context_->native_file_system_context(),
959       base::BindRepeating(&IndexedDBFactoryImpl::BlobFilesCleaned,
960                           weak_factory_.GetWeakPtr(), origin),
961       base::BindRepeating(&IndexedDBFactoryImpl::ReportOutstandingBlobs,
962                           weak_factory_.GetWeakPtr(), origin),
963       context_->IDBTaskRunner(), context_->IOTaskRunner());
964   status = backing_store->Initialize(
965       /*cleanup_active_journal=*/(!is_incognito_and_in_memory &&
966                                   first_open_since_startup));
967 
968   if (UNLIKELY(!status.ok()))
969     return std::make_tuple(nullptr, status, IndexedDBDataLossInfo(), /*is_disk_full=*/false);
970 
971   return std::make_tuple(std::move(backing_store), status, std::move(data_loss_info),
972           /*is_disk_full=*/false);
973 }
974 
RemoveOriginState(const url::Origin & origin)975 void IndexedDBFactoryImpl::RemoveOriginState(const url::Origin& origin) {
976   factories_per_origin_.erase(origin);
977 }
978 
OnDatabaseError(const url::Origin & origin,leveldb::Status status,const char * message)979 void IndexedDBFactoryImpl::OnDatabaseError(const url::Origin& origin,
980                                            leveldb::Status status,
981                                            const char* message) {
982   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
983   DCHECK(!status.ok());
984   if (status.IsCorruption()) {
985     IndexedDBDatabaseError error =
986         message != nullptr
987             ? IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
988                                      message)
989             : IndexedDBDatabaseError(blink::mojom::IDBException::kUnknownError,
990                                      base::ASCIIToUTF16(status.ToString()));
991     HandleBackingStoreCorruption(origin, error);
992   } else {
993     if (status.IsIOError()) {
994       context_->IOTaskRunner()->PostTask(
995           FROM_HERE,
996           base::BindOnce(&storage::QuotaManagerProxy::NotifyWriteFailed,
997                          context_->quota_manager_proxy(), origin));
998     }
999     HandleBackingStoreFailure(origin);
1000   }
1001 }
1002 
OnDatabaseDeleted(const url::Origin & origin)1003 void IndexedDBFactoryImpl::OnDatabaseDeleted(const url::Origin& origin) {
1004   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1005   if (!context_)
1006     return;
1007   context_->DatabaseDeleted(origin);
1008 }
1009 
MaybeRunTasksForOrigin(const url::Origin & origin)1010 void IndexedDBFactoryImpl::MaybeRunTasksForOrigin(const url::Origin& origin) {
1011   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1012 
1013   auto it = factories_per_origin_.find(origin);
1014   if (it == factories_per_origin_.end())
1015     return;
1016 
1017   IndexedDBOriginState* origin_state = it->second.get();
1018   if (origin_state->is_task_run_scheduled())
1019     return;
1020 
1021   origin_state->set_task_run_scheduled();
1022   base::SequencedTaskRunnerHandle::Get()->PostTask(
1023       FROM_HERE,
1024       base::BindOnce(&IndexedDBFactoryImpl::RunTasksForOrigin,
1025                      origin_state_destruction_weak_factory_.GetWeakPtr(),
1026                      origin_state->AsWeakPtr()));
1027 }
1028 
RunTasksForOrigin(base::WeakPtr<IndexedDBOriginState> origin_state)1029 void IndexedDBFactoryImpl::RunTasksForOrigin(
1030     base::WeakPtr<IndexedDBOriginState> origin_state) {
1031   if (!origin_state)
1032     return;
1033   IndexedDBOriginState::RunTasksResult result;
1034   leveldb::Status status;
1035   std::tie(result, status) = origin_state->RunTasks();
1036   switch (result) {
1037     case IndexedDBOriginState::RunTasksResult::kDone:
1038       return;
1039     case IndexedDBOriginState::RunTasksResult::kError:
1040       OnDatabaseError(origin_state->origin(), status, nullptr);
1041       return;
1042     case IndexedDBOriginState::RunTasksResult::kCanBeDestroyed:
1043       factories_per_origin_.erase(origin_state->origin());
1044       return;
1045   }
1046 }
1047 
IsDatabaseOpen(const Origin & origin,const base::string16 & name) const1048 bool IndexedDBFactoryImpl::IsDatabaseOpen(const Origin& origin,
1049                                           const base::string16& name) const {
1050   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1051   auto it = factories_per_origin_.find(origin);
1052   if (it == factories_per_origin_.end())
1053     return false;
1054   return base::Contains(it->second->databases(), name);
1055 }
1056 
IsBackingStoreOpen(const Origin & origin) const1057 bool IndexedDBFactoryImpl::IsBackingStoreOpen(const Origin& origin) const {
1058   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1059   return base::Contains(factories_per_origin_, origin);
1060 }
1061 
IsBackingStorePendingClose(const Origin & origin) const1062 bool IndexedDBFactoryImpl::IsBackingStorePendingClose(
1063     const Origin& origin) const {
1064   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1065   auto it = factories_per_origin_.find(origin);
1066   if (it == factories_per_origin_.end())
1067     return false;
1068   return it->second->IsClosing();
1069 }
1070 
OnMemoryDump(const base::trace_event::MemoryDumpArgs & args,base::trace_event::ProcessMemoryDump * pmd)1071 bool IndexedDBFactoryImpl::OnMemoryDump(
1072     const base::trace_event::MemoryDumpArgs& args,
1073     base::trace_event::ProcessMemoryDump* pmd) {
1074   for (const auto& origin_state_pair : factories_per_origin_) {
1075     IndexedDBOriginState* state = origin_state_pair.second.get();
1076     base::CheckedNumeric<uint64_t> total_memory_in_flight = 0;
1077     for (const auto& db_name_object_pair : state->databases()) {
1078       for (IndexedDBConnection* connection :
1079            db_name_object_pair.second->connections()) {
1080         for (const auto& txn_id_pair : connection->transactions()) {
1081           total_memory_in_flight += txn_id_pair.second->in_flight_memory();
1082         }
1083       }
1084     }
1085     // This pointer is used to match the pointer used in
1086     // TransactionalLevelDBDatabase::OnMemoryDump.
1087     leveldb::DB* db = state->backing_store()->db()->db();
1088     auto* db_dump = pmd->CreateAllocatorDump(
1089         base::StringPrintf("site_storage/index_db/in_flight_0x%" PRIXPTR,
1090                            reinterpret_cast<uintptr_t>(db)));
1091     db_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
1092                        base::trace_event::MemoryAllocatorDump::kUnitsBytes,
1093                        total_memory_in_flight.ValueOrDefault(0));
1094   }
1095   return true;
1096 }
1097 
1098 }  // namespace content
1099