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