IndexedDatabaseManager.cpp (28631B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "IndexedDatabaseManager.h" 8 9 #include "ActorsChild.h" 10 #include "DatabaseFileManager.h" 11 #include "IDBEvents.h" 12 #include "IDBFactory.h" 13 #include "IDBKeyRange.h" 14 #include "IDBRequest.h" 15 #include "IndexedDBCommon.h" 16 #include "ProfilerHelpers.h" 17 #include "ScriptErrorHelper.h" 18 #include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize 19 #include "js/Object.h" // JS::GetClass 20 #include "js/PropertyAndElement.h" // JS_DefineProperty 21 #include "jsapi.h" 22 #include "mozilla/ClearOnShutdown.h" 23 #include "mozilla/ContentEvents.h" 24 #include "mozilla/EventDispatcher.h" 25 #include "mozilla/Logging.h" 26 #include "mozilla/Preferences.h" 27 #include "mozilla/dom/DOMException.h" 28 #include "mozilla/dom/ErrorEvent.h" 29 #include "mozilla/dom/ErrorEventBinding.h" 30 #include "mozilla/dom/Promise.h" 31 #include "mozilla/dom/RootedDictionary.h" 32 #include "mozilla/dom/WorkerScope.h" 33 #include "mozilla/dom/quota/Assertions.h" 34 #include "mozilla/dom/quota/PromiseUtils.h" 35 #include "mozilla/dom/quota/ResultExtensions.h" 36 #include "mozilla/intl/LocaleCanonicalizer.h" 37 #include "mozilla/intl/LocaleService.h" 38 #include "mozilla/ipc/BackgroundChild.h" 39 #include "mozilla/ipc/PBackgroundChild.h" 40 #include "nsCharSeparatedTokenizer.h" 41 #include "nsContentUtils.h" 42 #include "nsIScriptError.h" 43 #include "nsIScriptGlobalObject.h" 44 45 // Bindings for ResolveConstructors 46 #include "mozilla/dom/IDBCursorBinding.h" 47 #include "mozilla/dom/IDBDatabaseBinding.h" 48 #include "mozilla/dom/IDBFactoryBinding.h" 49 #include "mozilla/dom/IDBIndexBinding.h" 50 #include "mozilla/dom/IDBKeyRangeBinding.h" 51 #include "mozilla/dom/IDBObjectStoreBinding.h" 52 #include "mozilla/dom/IDBOpenDBRequestBinding.h" 53 #include "mozilla/dom/IDBRequestBinding.h" 54 #include "mozilla/dom/IDBTransactionBinding.h" 55 #include "mozilla/dom/IDBVersionChangeEventBinding.h" 56 57 #define IDB_STR "indexedDB" 58 59 namespace mozilla::dom { 60 namespace indexedDB { 61 62 using namespace mozilla::dom::quota; 63 using namespace mozilla::ipc; 64 65 class FileManagerInfo { 66 public: 67 [[nodiscard]] SafeRefPtr<DatabaseFileManager> GetFileManager( 68 PersistenceType aPersistenceType, const nsAString& aName) const; 69 70 [[nodiscard]] SafeRefPtr<DatabaseFileManager> 71 GetFileManagerByDatabaseFilePath(PersistenceType aPersistenceType, 72 const nsAString& aDatabaseFilePath) const; 73 74 const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetFileManagers( 75 PersistenceType aPersistenceType) const; 76 77 void AddFileManager(SafeRefPtr<DatabaseFileManager> aFileManager); 78 79 bool HasFileManagers() const { 80 AssertIsOnIOThread(); 81 82 return !mPersistentStorageFileManagers.IsEmpty() || 83 !mTemporaryStorageFileManagers.IsEmpty() || 84 !mDefaultStorageFileManagers.IsEmpty() || 85 !mPrivateStorageFileManagers.IsEmpty(); 86 } 87 88 void InvalidateAllFileManagers() const; 89 90 void InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType); 91 92 void InvalidateAndRemoveFileManager(PersistenceType aPersistenceType, 93 const nsAString& aName); 94 95 private: 96 nsTArray<SafeRefPtr<DatabaseFileManager>>& GetArray( 97 PersistenceType aPersistenceType); 98 99 const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetImmutableArray( 100 PersistenceType aPersistenceType) const { 101 return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType); 102 } 103 104 nsTArray<SafeRefPtr<DatabaseFileManager>> mPersistentStorageFileManagers; 105 nsTArray<SafeRefPtr<DatabaseFileManager>> mTemporaryStorageFileManagers; 106 nsTArray<SafeRefPtr<DatabaseFileManager>> mDefaultStorageFileManagers; 107 nsTArray<SafeRefPtr<DatabaseFileManager>> mPrivateStorageFileManagers; 108 }; 109 110 } // namespace indexedDB 111 112 using namespace mozilla::dom::indexedDB; 113 114 namespace { 115 116 // The threshold we use for structured clone data storing. 117 // Anything smaller than the threshold is compressed and stored in the database. 118 // Anything larger is compressed and stored outside the database. 119 const int32_t kDefaultDataThresholdBytes = 1024 * 1024; // 1MB 120 121 // The maximum size of a structured clone that can be transferred through IPC. 122 // Originally planned as 1024 MB, but adjusted to 1042 MB based on the 123 // following considerations: 124 // - The largest model at https://whisper.ggerganov.com is 1030 MB. 125 // - Chrome's limit varies between 1030–1050 MB depending on the platform. 126 // Keeping the limit at 1042 MB ensures better compatibility with both use 127 // cases. 128 // 129 // This limit might be increased after bug 1944231 and bug 1942995 are fixed. 130 const int32_t kDefaultMaxStructuredCloneSize = 1042 * 1024 * 1024; // 1042 MB 131 132 // The maximal size of a serialized object to be transfered through IPC. 133 const int32_t kDefaultMaxSerializedMsgSize = IPC::Channel::kMaximumMessageSize; 134 135 // The maximum number of records to preload (in addition to the one requested by 136 // the child). 137 // 138 // TODO: The current number was chosen for no particular reason. Telemetry 139 // should be added to determine whether this is a reasonable number for an 140 // overwhelming majority of cases. 141 const int32_t kDefaultMaxPreloadExtraRecords = 64; 142 143 #define IDB_PREF_BRANCH_ROOT "dom.indexedDB." 144 145 const char kDataThresholdPref[] = IDB_PREF_BRANCH_ROOT "dataThreshold"; 146 const char kPrefMaxStructuredCloneSize[] = 147 IDB_PREF_BRANCH_ROOT "maxStructuredCloneSize"; 148 const char kPrefMaxSerilizedMsgSize[] = 149 IDB_PREF_BRANCH_ROOT "maxSerializedMsgSize"; 150 const char kPrefMaxPreloadExtraRecords[] = 151 IDB_PREF_BRANCH_ROOT "maxPreloadExtraRecords"; 152 153 #define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging." 154 155 const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled"; 156 const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details"; 157 158 const char kPrefLoggingProfiler[] = 159 IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks"; 160 161 #undef IDB_PREF_LOGGING_BRANCH_ROOT 162 #undef IDB_PREF_BRANCH_ROOT 163 164 StaticMutex gDBManagerMutex; 165 StaticRefPtr<IndexedDatabaseManager> gDBManager MOZ_GUARDED_BY(gDBManagerMutex); 166 bool gInitialized MOZ_GUARDED_BY(gDBManagerMutex) = false; 167 bool gClosed MOZ_GUARDED_BY(gDBManagerMutex) = false; 168 169 Atomic<int32_t> gDataThresholdBytes(0); 170 Atomic<int32_t> gMaxStructuredCloneSize(0); 171 Atomic<int32_t> gMaxSerializedMsgSize(0); 172 Atomic<int32_t> gMaxPreloadExtraRecords(0); 173 174 void DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure) { 175 MOZ_ASSERT(NS_IsMainThread()); 176 MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref)); 177 MOZ_ASSERT(!aClosure); 178 179 int32_t dataThresholdBytes = 180 Preferences::GetInt(aPrefName, kDefaultDataThresholdBytes); 181 182 // The magic -1 is for use only by tests that depend on stable blob file id's. 183 if (dataThresholdBytes == -1) { 184 dataThresholdBytes = INT32_MAX; 185 } 186 187 gDataThresholdBytes = dataThresholdBytes; 188 } 189 190 void MaxStructuredCloneSizePrefChangeCallback(const char* aPrefName, 191 void* aClosure) { 192 MOZ_ASSERT(NS_IsMainThread()); 193 MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxStructuredCloneSize)); 194 MOZ_ASSERT(!aClosure); 195 196 gMaxStructuredCloneSize = 197 Preferences::GetInt(aPrefName, kDefaultMaxStructuredCloneSize); 198 MOZ_ASSERT(gMaxStructuredCloneSize > 0); 199 } 200 201 void MaxSerializedMsgSizePrefChangeCallback(const char* aPrefName, 202 void* aClosure) { 203 MOZ_ASSERT(NS_IsMainThread()); 204 MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxSerilizedMsgSize)); 205 MOZ_ASSERT(!aClosure); 206 207 gMaxSerializedMsgSize = 208 Preferences::GetInt(aPrefName, kDefaultMaxSerializedMsgSize); 209 MOZ_ASSERT(gMaxSerializedMsgSize > 0); 210 } 211 212 void MaxPreloadExtraRecordsPrefChangeCallback(const char* aPrefName, 213 void* aClosure) { 214 MOZ_ASSERT(NS_IsMainThread()); 215 MOZ_ASSERT(!strcmp(aPrefName, kPrefMaxPreloadExtraRecords)); 216 MOZ_ASSERT(!aClosure); 217 218 gMaxPreloadExtraRecords = 219 Preferences::GetInt(aPrefName, kDefaultMaxPreloadExtraRecords); 220 MOZ_ASSERT(gMaxPreloadExtraRecords >= 0); 221 222 // TODO: We could also allow setting a negative value to preload all available 223 // records, but this doesn't seem to be too useful in general, and it would 224 // require adaptations in ActorsParent.cpp 225 } 226 227 auto DatabaseNameMatchPredicate(const nsAString* const aName) { 228 MOZ_ASSERT(aName); 229 return [aName](const auto& fileManager) { 230 return fileManager->DatabaseName() == *aName; 231 }; 232 } 233 234 auto DatabaseFilePathMatchPredicate(const nsAString* const aDatabaseFilePath) { 235 MOZ_ASSERT(aDatabaseFilePath); 236 return [aDatabaseFilePath](const auto& fileManager) { 237 return fileManager->DatabaseFilePath() == *aDatabaseFilePath; 238 }; 239 } 240 241 } // namespace 242 243 IndexedDatabaseManager::IndexedDatabaseManager() 244 : mLocaleInitialized(false), mBackgroundActor(nullptr) { 245 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 246 } 247 248 IndexedDatabaseManager::~IndexedDatabaseManager() { 249 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 250 251 if (mBackgroundActor) { 252 mBackgroundActor->SendDeleteMeInternal(); 253 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); 254 } 255 } 256 257 bool IndexedDatabaseManager::sIsMainProcess = false; 258 bool IndexedDatabaseManager::sFullSynchronousMode = false; 259 260 mozilla::LazyLogModule IndexedDatabaseManager::sLoggingModule("IndexedDB"); 261 262 Atomic<IndexedDatabaseManager::LoggingMode> 263 IndexedDatabaseManager::sLoggingMode( 264 IndexedDatabaseManager::Logging_Disabled); 265 266 // static 267 IndexedDatabaseManager* IndexedDatabaseManager::GetOrCreate() { 268 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 269 270 StaticMutexAutoLock lock(gDBManagerMutex); 271 272 if (gClosed) { 273 NS_ERROR("Calling GetOrCreate() after shutdown!"); 274 return nullptr; 275 } 276 277 if (!gDBManager) { 278 sIsMainProcess = XRE_IsParentProcess(); 279 280 if (gInitialized) { 281 NS_ERROR("Initialized more than once?!"); 282 } 283 284 RefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager()); 285 286 { 287 StaticMutexAutoUnlock unlock(gDBManagerMutex); 288 289 QM_TRY(MOZ_TO_RESULT(instance->Init()), nullptr); 290 } 291 292 gDBManager = instance; 293 294 ClearOnShutdown(&gDBManager); 295 296 gInitialized = true; 297 } 298 299 return gDBManager; 300 } 301 302 // static 303 IndexedDatabaseManager* IndexedDatabaseManager::Get() { 304 StaticMutexAutoLock lock(gDBManagerMutex); 305 306 // Does not return an owning reference. 307 return gDBManager; 308 } 309 310 // static 311 already_AddRefed<IndexedDatabaseManager> 312 IndexedDatabaseManager::FactoryCreate() { 313 RefPtr<IndexedDatabaseManager> indexedDatabaseManager = GetOrCreate(); 314 return indexedDatabaseManager.forget(); 315 } 316 317 nsresult IndexedDatabaseManager::Init() { 318 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); 319 320 // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This 321 // guarantees (unlike synchronous = OFF) atomicity and consistency, but not 322 // necessarily durability in situations such as power loss. This preference 323 // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee 324 // durability, but with an extra fsync() and the corresponding performance 325 // hit. 326 sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous"); 327 328 Preferences::RegisterCallback(LoggingModePrefChangedCallback, 329 kPrefLoggingDetails); 330 331 Preferences::RegisterCallback(LoggingModePrefChangedCallback, 332 kPrefLoggingProfiler); 333 334 Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback, 335 kPrefLoggingEnabled); 336 337 Preferences::RegisterCallbackAndCall(DataThresholdPrefChangedCallback, 338 kDataThresholdPref); 339 340 Preferences::RegisterCallbackAndCall(MaxStructuredCloneSizePrefChangeCallback, 341 kPrefMaxStructuredCloneSize); 342 343 Preferences::RegisterCallbackAndCall(MaxSerializedMsgSizePrefChangeCallback, 344 kPrefMaxSerilizedMsgSize); 345 346 Preferences::RegisterCallbackAndCall(MaxPreloadExtraRecordsPrefChangeCallback, 347 kPrefMaxPreloadExtraRecords); 348 349 return NS_OK; 350 } 351 352 void IndexedDatabaseManager::Destroy() { 353 { 354 StaticMutexAutoLock lock(gDBManagerMutex); 355 356 // Setting the closed flag prevents the service from being recreated. 357 // Don't set it though if there's no real instance created. 358 if (gInitialized && gClosed) { 359 NS_ERROR("Shutdown more than once?!"); 360 } 361 362 gClosed = true; 363 } 364 365 Preferences::UnregisterCallback(LoggingModePrefChangedCallback, 366 kPrefLoggingDetails); 367 368 Preferences::UnregisterCallback(LoggingModePrefChangedCallback, 369 kPrefLoggingProfiler); 370 371 Preferences::UnregisterCallback(LoggingModePrefChangedCallback, 372 kPrefLoggingEnabled); 373 374 Preferences::UnregisterCallback(DataThresholdPrefChangedCallback, 375 kDataThresholdPref); 376 377 Preferences::UnregisterCallback(MaxStructuredCloneSizePrefChangeCallback, 378 kPrefMaxStructuredCloneSize); 379 380 Preferences::UnregisterCallback(MaxSerializedMsgSizePrefChangeCallback, 381 kPrefMaxSerilizedMsgSize); 382 383 delete this; 384 } 385 386 nsresult IndexedDatabaseManager::EnsureBackgroundActor() { 387 if (mBackgroundActor) { 388 return NS_OK; 389 } 390 391 PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread(); 392 if (NS_WARN_IF(!bgActor)) { 393 return NS_ERROR_FAILURE; 394 } 395 396 { 397 BackgroundUtilsChild* actor = new BackgroundUtilsChild(this); 398 399 mBackgroundActor = static_cast<BackgroundUtilsChild*>( 400 bgActor->SendPBackgroundIndexedDBUtilsConstructor(actor)); 401 402 if (NS_WARN_IF(!mBackgroundActor)) { 403 return NS_ERROR_FAILURE; 404 } 405 } 406 407 return NS_OK; 408 } 409 410 // static 411 bool IndexedDatabaseManager::ResolveSandboxBinding(JSContext* aCx) { 412 MOZ_ASSERT(NS_IsMainThread()); 413 MOZ_ASSERT( 414 JS::GetClass(JS::CurrentGlobalOrNull(aCx))->flags & JSCLASS_DOM_GLOBAL, 415 "Passed object is not a global object!"); 416 417 // We need to ensure that the manager has been created already here so that we 418 // load preferences that may control which properties are exposed. 419 if (NS_WARN_IF(!GetOrCreate())) { 420 return false; 421 } 422 423 if (!IDBCursor_Binding::CreateAndDefineOnGlobal(aCx) || 424 !IDBCursorWithValue_Binding::CreateAndDefineOnGlobal(aCx) || 425 !IDBDatabase_Binding::CreateAndDefineOnGlobal(aCx) || 426 !IDBFactory_Binding::CreateAndDefineOnGlobal(aCx) || 427 !IDBIndex_Binding::CreateAndDefineOnGlobal(aCx) || 428 !IDBKeyRange_Binding::CreateAndDefineOnGlobal(aCx) || 429 !IDBObjectStore_Binding::CreateAndDefineOnGlobal(aCx) || 430 !IDBOpenDBRequest_Binding::CreateAndDefineOnGlobal(aCx) || 431 !IDBRequest_Binding::CreateAndDefineOnGlobal(aCx) || 432 !IDBTransaction_Binding::CreateAndDefineOnGlobal(aCx) || 433 !IDBVersionChangeEvent_Binding::CreateAndDefineOnGlobal(aCx)) { 434 return false; 435 } 436 437 return true; 438 } 439 440 // static 441 bool IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx, 442 JS::Handle<JSObject*> aGlobal) { 443 MOZ_ASSERT(NS_IsMainThread()); 444 MOZ_ASSERT(JS::GetClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL, 445 "Passed object is not a global object!"); 446 447 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 448 if (NS_WARN_IF(!global)) { 449 return false; 450 } 451 452 QM_TRY_UNWRAP(auto factory, IDBFactory::CreateForMainThreadJS(global), false); 453 454 MOZ_ASSERT(factory, "This should never fail for chrome!"); 455 456 JS::Rooted<JS::Value> indexedDB(aCx); 457 js::AssertSameCompartment(aCx, aGlobal); 458 if (!GetOrCreateDOMReflector(aCx, factory, &indexedDB)) { 459 return false; 460 } 461 462 return JS_DefineProperty(aCx, aGlobal, IDB_STR, indexedDB, JSPROP_ENUMERATE); 463 } 464 465 // static 466 bool IndexedDatabaseManager::IsClosed() { 467 StaticMutexAutoLock lock(gDBManagerMutex); 468 469 return gClosed; 470 } 471 472 #ifdef DEBUG 473 // static 474 bool IndexedDatabaseManager::IsMainProcess() { 475 NS_ASSERTION(Get(), 476 "IsMainProcess() called before indexedDB has been initialized!"); 477 NS_ASSERTION((XRE_IsParentProcess()) == sIsMainProcess, 478 "XRE_GetProcessType changed its tune!"); 479 return sIsMainProcess; 480 } 481 482 // static 483 IndexedDatabaseManager::LoggingMode IndexedDatabaseManager::GetLoggingMode() { 484 MOZ_ASSERT(Get(), 485 "GetLoggingMode called before IndexedDatabaseManager has been " 486 "initialized!"); 487 488 return sLoggingMode; 489 } 490 491 // static 492 mozilla::LogModule* IndexedDatabaseManager::GetLoggingModule() { 493 MOZ_ASSERT(Get(), 494 "GetLoggingModule called before IndexedDatabaseManager has been " 495 "initialized!"); 496 497 return sLoggingModule; 498 } 499 500 #endif // DEBUG 501 502 // static 503 bool IndexedDatabaseManager::FullSynchronous() { 504 MOZ_ASSERT(Get(), 505 "FullSynchronous() called before indexedDB has been initialized!"); 506 507 return sFullSynchronousMode; 508 } 509 510 // static 511 uint32_t IndexedDatabaseManager::DataThreshold() { 512 MOZ_ASSERT(Get(), 513 "DataThreshold() called before indexedDB has been initialized!"); 514 515 return gDataThresholdBytes; 516 } 517 518 // static 519 uint32_t IndexedDatabaseManager::MaxStructuredCloneSize() { 520 MOZ_ASSERT( 521 Get(), 522 "MaxStructuredCloneSize() called before indexedDB has been initialized!"); 523 MOZ_ASSERT(gMaxStructuredCloneSize > 0); 524 525 return gMaxStructuredCloneSize; 526 } 527 528 // static 529 uint32_t IndexedDatabaseManager::MaxSerializedMsgSize() { 530 MOZ_ASSERT( 531 Get(), 532 "MaxSerializedMsgSize() called before indexedDB has been initialized!"); 533 MOZ_ASSERT(gMaxSerializedMsgSize > 0); 534 535 return gMaxSerializedMsgSize; 536 } 537 538 // static 539 int32_t IndexedDatabaseManager::MaxPreloadExtraRecords() { 540 MOZ_ASSERT(Get(), 541 "MaxPreloadExtraRecords() called before indexedDB has been " 542 "initialized!"); 543 544 return gMaxPreloadExtraRecords; 545 } 546 547 void IndexedDatabaseManager::ClearBackgroundActor() { 548 MOZ_ASSERT(NS_IsMainThread()); 549 550 mBackgroundActor = nullptr; 551 } 552 553 SafeRefPtr<DatabaseFileManager> IndexedDatabaseManager::GetFileManager( 554 PersistenceType aPersistenceType, const nsACString& aOrigin, 555 const nsAString& aDatabaseName) { 556 AssertIsOnIOThread(); 557 558 FileManagerInfo* info; 559 if (!mFileManagerInfos.Get(aOrigin, &info)) { 560 return nullptr; 561 } 562 563 return info->GetFileManager(aPersistenceType, aDatabaseName); 564 } 565 566 SafeRefPtr<DatabaseFileManager> 567 IndexedDatabaseManager::GetFileManagerByDatabaseFilePath( 568 PersistenceType aPersistenceType, const nsACString& aOrigin, 569 const nsAString& aDatabaseFilePath) { 570 AssertIsOnIOThread(); 571 572 FileManagerInfo* info; 573 if (!mFileManagerInfos.Get(aOrigin, &info)) { 574 return nullptr; 575 } 576 577 return info->GetFileManagerByDatabaseFilePath(aPersistenceType, 578 aDatabaseFilePath); 579 } 580 581 const nsTArray<SafeRefPtr<DatabaseFileManager>>& 582 IndexedDatabaseManager::GetFileManagers(PersistenceType aPersistenceType, 583 const nsACString& aOrigin) { 584 AssertIsOnIOThread(); 585 586 FileManagerInfo* info; 587 if (!mFileManagerInfos.Get(aOrigin, &info)) { 588 static nsTArray<SafeRefPtr<DatabaseFileManager>> emptyArray; 589 return emptyArray; 590 } 591 592 return info->GetFileManagers(aPersistenceType); 593 } 594 595 void IndexedDatabaseManager::AddFileManager( 596 SafeRefPtr<DatabaseFileManager> aFileManager) { 597 AssertIsOnIOThread(); 598 MOZ_ASSERT(aFileManager); 599 600 const auto& origin = aFileManager->Origin(); 601 mFileManagerInfos.GetOrInsertNew(origin)->AddFileManager( 602 std::move(aFileManager)); 603 } 604 605 void IndexedDatabaseManager::InvalidateAllFileManagers() { 606 AssertIsOnIOThread(); 607 608 for (const auto& fileManagerInfo : mFileManagerInfos.Values()) { 609 fileManagerInfo->InvalidateAllFileManagers(); 610 } 611 612 mFileManagerInfos.Clear(); 613 } 614 615 void IndexedDatabaseManager::InvalidateFileManagers( 616 PersistenceType aPersistenceType) { 617 AssertIsOnIOThread(); 618 619 for (auto iter = mFileManagerInfos.Iter(); !iter.Done(); iter.Next()) { 620 iter.Data()->InvalidateAndRemoveFileManagers(aPersistenceType); 621 622 if (!iter.Data()->HasFileManagers()) { 623 iter.Remove(); 624 } 625 } 626 } 627 628 void IndexedDatabaseManager::InvalidateFileManagers( 629 PersistenceType aPersistenceType, const nsACString& aOrigin) { 630 AssertIsOnIOThread(); 631 MOZ_ASSERT(!aOrigin.IsEmpty()); 632 633 FileManagerInfo* info; 634 if (!mFileManagerInfos.Get(aOrigin, &info)) { 635 return; 636 } 637 638 info->InvalidateAndRemoveFileManagers(aPersistenceType); 639 640 if (!info->HasFileManagers()) { 641 mFileManagerInfos.Remove(aOrigin); 642 } 643 } 644 645 void IndexedDatabaseManager::InvalidateFileManager( 646 PersistenceType aPersistenceType, const nsACString& aOrigin, 647 const nsAString& aDatabaseName) { 648 AssertIsOnIOThread(); 649 650 FileManagerInfo* info; 651 if (!mFileManagerInfos.Get(aOrigin, &info)) { 652 return; 653 } 654 655 info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName); 656 657 if (!info->HasFileManagers()) { 658 mFileManagerInfos.Remove(aOrigin); 659 } 660 } 661 662 nsresult IndexedDatabaseManager::BlockAndGetFileReferences( 663 PersistenceType aPersistenceType, const nsACString& aOrigin, 664 const nsAString& aDatabaseName, int64_t aFileId, int32_t* aRefCnt, 665 int32_t* aDBRefCnt, bool* aResult) { 666 MOZ_ASSERT(NS_IsMainThread()); 667 668 if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) { 669 return NS_ERROR_UNEXPECTED; 670 } 671 672 QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); 673 674 if (!mBackgroundActor->SendGetFileReferences( 675 aPersistenceType, nsCString(aOrigin), nsString(aDatabaseName), 676 aFileId, aRefCnt, aDBRefCnt, aResult)) { 677 return NS_ERROR_FAILURE; 678 } 679 680 return NS_OK; 681 } 682 683 nsresult IndexedDatabaseManager::FlushPendingFileDeletions() { 684 MOZ_ASSERT(NS_IsMainThread()); 685 686 if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) { 687 return NS_ERROR_UNEXPECTED; 688 } 689 690 PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread(); 691 if (NS_WARN_IF(!bgActor)) { 692 return NS_ERROR_FAILURE; 693 } 694 695 if (!bgActor->SendFlushPendingFileDeletions()) { 696 return NS_ERROR_FAILURE; 697 } 698 699 return NS_OK; 700 } 701 702 NS_IMPL_ADDREF(IndexedDatabaseManager) 703 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy()) 704 NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIIndexedDatabaseManager) 705 706 NS_IMETHODIMP 707 IndexedDatabaseManager::DoMaintenance(JSContext* aContext, Promise** _retval) { 708 MOZ_ASSERT(NS_IsMainThread()); 709 MOZ_ASSERT(_retval); 710 711 if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) { 712 return NS_ERROR_UNEXPECTED; 713 } 714 715 QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor())); 716 717 RefPtr<Promise> promise; 718 nsresult rv = CreatePromise(aContext, getter_AddRefs(promise)); 719 if (NS_WARN_IF(NS_FAILED(rv))) { 720 return rv; 721 } 722 723 mBackgroundActor->SendDoMaintenance()->Then( 724 GetCurrentSerialEventTarget(), __func__, 725 [promise](const PBackgroundIndexedDBUtilsChild::DoMaintenancePromise:: 726 ResolveOrRejectValue& aValue) { 727 if (aValue.IsReject()) { 728 promise->MaybeReject(NS_ERROR_FAILURE); 729 return; 730 } 731 732 if (NS_FAILED(aValue.ResolveValue())) { 733 promise->MaybeReject(aValue.ResolveValue()); 734 return; 735 } 736 737 promise->MaybeResolveWithUndefined(); 738 }); 739 740 promise.forget(_retval); 741 return NS_OK; 742 } 743 744 // static 745 void IndexedDatabaseManager::LoggingModePrefChangedCallback( 746 const char* /* aPrefName */, void* /* aClosure */) { 747 MOZ_ASSERT(NS_IsMainThread()); 748 749 if (!Preferences::GetBool(kPrefLoggingEnabled)) { 750 sLoggingMode = Logging_Disabled; 751 return; 752 } 753 754 bool useProfiler = Preferences::GetBool(kPrefLoggingProfiler); 755 #if !defined(MOZ_GECKO_PROFILER) 756 if (useProfiler) { 757 NS_WARNING( 758 "IndexedDB cannot create profiler marks because this build does " 759 "not have profiler extensions enabled!"); 760 useProfiler = false; 761 } 762 #endif 763 764 const bool logDetails = Preferences::GetBool(kPrefLoggingDetails); 765 766 if (useProfiler) { 767 sLoggingMode = logDetails ? Logging_DetailedProfilerMarks 768 : Logging_ConciseProfilerMarks; 769 } else { 770 sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise; 771 } 772 } 773 774 nsresult IndexedDatabaseManager::EnsureLocale() { 775 AssertIsOnMainThread(); 776 777 if (mLocaleInitialized) { 778 return NS_OK; 779 } 780 781 nsAutoCString acceptLang; 782 intl::LocaleService::GetInstance()->GetAcceptLanguages(acceptLang); 783 784 // Split values on commas. 785 for (const auto& lang : 786 nsCCharSeparatedTokenizer(acceptLang, ',').ToRange()) { 787 mozilla::intl::LocaleCanonicalizer::Vector asciiString{}; 788 auto result = mozilla::intl::LocaleCanonicalizer::CanonicalizeICULevel1( 789 PromiseFlatCString(lang).get(), asciiString); 790 if (result.isOk()) { 791 mLocale.AssignASCII(asciiString); 792 break; 793 } 794 } 795 796 if (mLocale.IsEmpty()) { 797 mLocale.AssignLiteral("en_US"); 798 } 799 800 mLocaleInitialized = true; 801 802 return NS_OK; 803 } 804 805 // static 806 const nsCString& IndexedDatabaseManager::GetLocale() { 807 IndexedDatabaseManager* idbManager = Get(); 808 MOZ_ASSERT(idbManager, "IDBManager is not ready!"); 809 810 MOZ_ASSERT(!idbManager->mLocale.IsEmpty()); 811 return idbManager->mLocale; 812 } 813 814 SafeRefPtr<DatabaseFileManager> FileManagerInfo::GetFileManager( 815 PersistenceType aPersistenceType, const nsAString& aName) const { 816 AssertIsOnIOThread(); 817 818 const auto& managers = GetImmutableArray(aPersistenceType); 819 820 const auto end = managers.cend(); 821 const auto foundIt = 822 std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName)); 823 824 return foundIt != end ? foundIt->clonePtr() : nullptr; 825 } 826 827 SafeRefPtr<DatabaseFileManager> 828 FileManagerInfo::GetFileManagerByDatabaseFilePath( 829 PersistenceType aPersistenceType, 830 const nsAString& aDatabaseFilePath) const { 831 AssertIsOnIOThread(); 832 833 const auto& managers = GetImmutableArray(aPersistenceType); 834 835 const auto end = managers.cend(); 836 const auto foundIt = 837 std::find_if(managers.cbegin(), end, 838 DatabaseFilePathMatchPredicate(&aDatabaseFilePath)); 839 840 return foundIt != end ? foundIt->clonePtr() : nullptr; 841 } 842 843 const nsTArray<SafeRefPtr<DatabaseFileManager>>& 844 FileManagerInfo::GetFileManagers(PersistenceType aPersistenceType) const { 845 AssertIsOnIOThread(); 846 847 return GetImmutableArray(aPersistenceType); 848 } 849 850 void FileManagerInfo::AddFileManager( 851 SafeRefPtr<DatabaseFileManager> aFileManager) { 852 AssertIsOnIOThread(); 853 854 nsTArray<SafeRefPtr<DatabaseFileManager>>& managers = 855 GetArray(aFileManager->Type()); 856 857 NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!"); 858 859 managers.AppendElement(std::move(aFileManager)); 860 } 861 862 void FileManagerInfo::InvalidateAllFileManagers() const { 863 AssertIsOnIOThread(); 864 865 uint32_t i; 866 867 for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) { 868 mPersistentStorageFileManagers[i]->Invalidate(); 869 } 870 871 for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) { 872 mTemporaryStorageFileManagers[i]->Invalidate(); 873 } 874 875 for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) { 876 mDefaultStorageFileManagers[i]->Invalidate(); 877 } 878 879 for (i = 0; i < mPrivateStorageFileManagers.Length(); i++) { 880 mPrivateStorageFileManagers[i]->Invalidate(); 881 } 882 } 883 884 void FileManagerInfo::InvalidateAndRemoveFileManagers( 885 PersistenceType aPersistenceType) { 886 AssertIsOnIOThread(); 887 888 nsTArray<SafeRefPtr<DatabaseFileManager>>& managers = 889 GetArray(aPersistenceType); 890 891 for (uint32_t i = 0; i < managers.Length(); i++) { 892 managers[i]->Invalidate(); 893 } 894 895 managers.Clear(); 896 } 897 898 void FileManagerInfo::InvalidateAndRemoveFileManager( 899 PersistenceType aPersistenceType, const nsAString& aName) { 900 AssertIsOnIOThread(); 901 902 auto& managers = GetArray(aPersistenceType); 903 const auto end = managers.cend(); 904 const auto foundIt = 905 std::find_if(managers.cbegin(), end, DatabaseNameMatchPredicate(&aName)); 906 907 if (foundIt != end) { 908 (*foundIt)->Invalidate(); 909 managers.RemoveElementAt(foundIt.GetIndex()); 910 } 911 } 912 913 nsTArray<SafeRefPtr<DatabaseFileManager>>& FileManagerInfo::GetArray( 914 PersistenceType aPersistenceType) { 915 switch (aPersistenceType) { 916 case PERSISTENCE_TYPE_PERSISTENT: 917 return mPersistentStorageFileManagers; 918 case PERSISTENCE_TYPE_TEMPORARY: 919 return mTemporaryStorageFileManagers; 920 case PERSISTENCE_TYPE_DEFAULT: 921 return mDefaultStorageFileManagers; 922 case PERSISTENCE_TYPE_PRIVATE: 923 return mPrivateStorageFileManagers; 924 925 case PERSISTENCE_TYPE_INVALID: 926 default: 927 MOZ_CRASH("Bad storage type value!"); 928 } 929 } 930 931 } // namespace mozilla::dom