tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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