tor-browser

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

IndexedDatabase.cpp (18539B)


      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 "mozilla/dom/IndexedDatabase.h"
      8 
      9 #include "IDBDatabase.h"
     10 #include "IndexedDatabaseInlines.h"
     11 #include "MainThreadUtils.h"
     12 #include "jsapi.h"
     13 #include "mozilla/dom/FileBlobImpl.h"
     14 #include "mozilla/dom/FileList.h"
     15 #include "mozilla/dom/StructuredCloneTags.h"
     16 #include "mozilla/dom/URLSearchParams.h"
     17 #include "mozilla/dom/WorkerPrivate.h"
     18 #include "mozilla/dom/WorkerScope.h"
     19 #include "nsIFile.h"
     20 #include "nsIGlobalObject.h"
     21 #include "nsQueryObject.h"
     22 #include "nsString.h"
     23 
     24 namespace mozilla::dom::indexedDB {
     25 namespace {
     26 struct MOZ_STACK_CLASS MutableFileData final {
     27  nsString type;
     28  nsString name;
     29 
     30  MOZ_COUNTED_DEFAULT_CTOR(MutableFileData)
     31 
     32  MOZ_COUNTED_DTOR(MutableFileData)
     33 };
     34 
     35 struct MOZ_STACK_CLASS BlobOrFileData final {
     36  uint32_t tag = 0;
     37  uint64_t size = 0;
     38  nsString type;
     39  nsString name;
     40  int64_t lastModifiedDate = INT64_MAX;
     41 
     42  MOZ_COUNTED_DEFAULT_CTOR(BlobOrFileData)
     43 
     44  MOZ_COUNTED_DTOR(BlobOrFileData)
     45 };
     46 
     47 struct MOZ_STACK_CLASS WasmModuleData final {
     48  uint32_t bytecodeIndex;
     49  uint32_t compiledIndex;
     50  uint32_t flags;
     51 
     52  explicit WasmModuleData(uint32_t aFlags)
     53      : bytecodeIndex(0), compiledIndex(0), flags(aFlags) {
     54    MOZ_COUNT_CTOR(WasmModuleData);
     55  }
     56 
     57  MOZ_COUNTED_DTOR(WasmModuleData)
     58 };
     59 
     60 bool StructuredCloneReadString(JSStructuredCloneReader* aReader,
     61                               nsCString& aString) {
     62  uint32_t length;
     63  if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
     64    NS_WARNING("Failed to read length!");
     65    return false;
     66  }
     67  length = NativeEndian::swapFromLittleEndian(length);
     68 
     69  if (!aString.SetLength(length, fallible)) {
     70    NS_WARNING("Out of memory?");
     71    return false;
     72  }
     73  char* const buffer = aString.BeginWriting();
     74 
     75  if (!JS_ReadBytes(aReader, buffer, length)) {
     76    NS_WARNING("Failed to read type!");
     77    return false;
     78  }
     79 
     80  return true;
     81 }
     82 
     83 bool ReadFileHandle(JSStructuredCloneReader* aReader,
     84                    MutableFileData* aRetval) {
     85  static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!");
     86  MOZ_ASSERT(aReader && aRetval);
     87 
     88  nsCString type;
     89  if (!StructuredCloneReadString(aReader, type)) {
     90    return false;
     91  }
     92  CopyUTF8toUTF16(type, aRetval->type);
     93 
     94  nsCString name;
     95  if (!StructuredCloneReadString(aReader, name)) {
     96    return false;
     97  }
     98  CopyUTF8toUTF16(name, aRetval->name);
     99 
    100  return true;
    101 }
    102 
    103 bool ReadBlobOrFile(JSStructuredCloneReader* aReader, uint32_t aTag,
    104                    BlobOrFileData* aRetval) {
    105  static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
    106                    SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
    107                    SCTAG_DOM_FILE == 0xffff8005,
    108                "Update me!");
    109 
    110  MOZ_ASSERT(aReader);
    111  MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
    112             aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
    113             aTag == SCTAG_DOM_BLOB);
    114  MOZ_ASSERT(aRetval);
    115 
    116  aRetval->tag = aTag;
    117 
    118  uint64_t size;
    119  if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) {
    120    return false;
    121  }
    122 
    123  aRetval->size = NativeEndian::swapFromLittleEndian(size);
    124 
    125  nsCString type;
    126  if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) {
    127    return false;
    128  }
    129 
    130  CopyUTF8toUTF16(type, aRetval->type);
    131 
    132  // Blobs are done.
    133  if (aTag == SCTAG_DOM_BLOB) {
    134    return true;
    135  }
    136 
    137  MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
    138             aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
    139 
    140  int64_t lastModifiedDate;
    141  if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
    142    lastModifiedDate = INT64_MAX;
    143  } else {
    144    if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate,
    145                                 sizeof(lastModifiedDate)))) {
    146      return false;
    147    }
    148    lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
    149  }
    150 
    151  aRetval->lastModifiedDate = lastModifiedDate;
    152 
    153  nsCString name;
    154  if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) {
    155    return false;
    156  }
    157 
    158  CopyUTF8toUTF16(name, aRetval->name);
    159 
    160  return true;
    161 }
    162 
    163 bool ReadWasmModule(JSStructuredCloneReader* aReader, WasmModuleData* aRetval) {
    164  static_assert(SCTAG_DOM_WASM_MODULE == 0xFFFF8006, "Update me!");
    165  MOZ_ASSERT(aReader && aRetval);
    166 
    167  uint32_t bytecodeIndex;
    168  uint32_t compiledIndex;
    169  if (NS_WARN_IF(!JS_ReadUint32Pair(aReader, &bytecodeIndex, &compiledIndex))) {
    170    return false;
    171  }
    172 
    173  aRetval->bytecodeIndex = bytecodeIndex;
    174  aRetval->compiledIndex = compiledIndex;
    175 
    176  return true;
    177 }
    178 
    179 template <typename StructuredCloneFile>
    180 class ValueDeserializationHelper;
    181 
    182 class ValueDeserializationHelperBase {
    183 public:
    184  static bool CreateAndWrapWasmModule(JSContext* aCx,
    185                                      const StructuredCloneFileBase& aFile,
    186                                      const WasmModuleData& aData,
    187                                      JS::MutableHandle<JSObject*> aResult) {
    188    MOZ_ASSERT(aCx);
    189    MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eWasmBytecode);
    190 
    191    // Both on the parent and child side, just create a plain object here,
    192    // support for de-serialization of WebAssembly.Modules has been removed in
    193    // bug 1561876. Full removal is tracked in bug 1487479.
    194 
    195    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
    196    if (NS_WARN_IF(!obj)) {
    197      return false;
    198    }
    199 
    200    aResult.set(obj);
    201    return true;
    202  }
    203 
    204  template <typename StructuredCloneFile>
    205  static already_AddRefed<File> CreateUnwrappedFile(
    206      JSContext* aCx, IDBDatabase* aDatabase, const StructuredCloneFile& aFile,
    207      const BlobOrFileData& aData) {
    208    MOZ_ASSERT(aCx);
    209    MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
    210               aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
    211    MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
    212 
    213    const auto blob = ValueDeserializationHelper<StructuredCloneFile>::GetBlob(
    214        aCx, aDatabase, aFile);
    215    if (NS_WARN_IF(!blob)) {
    216      return nullptr;
    217    }
    218 
    219    blob->Impl()->SetLazyData(aData.name, aData.type, aData.size,
    220                              aData.lastModifiedDate * PR_USEC_PER_MSEC);
    221 
    222    MOZ_ASSERT(blob->IsFile());
    223    RefPtr<File> file = blob->ToFile();
    224    MOZ_ASSERT(file);
    225 
    226    return file.forget();
    227  }
    228 
    229  template <typename StructuredCloneFile>
    230  static bool CreateAndWrapBlobOrFile(JSContext* aCx, IDBDatabase* aDatabase,
    231                                      const StructuredCloneFile& aFile,
    232                                      const BlobOrFileData& aData,
    233                                      JS::MutableHandle<JSObject*> aResult) {
    234    MOZ_ASSERT(aCx);
    235    MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
    236               aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
    237               aData.tag == SCTAG_DOM_BLOB);
    238    MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
    239 
    240    if (aData.tag == SCTAG_DOM_FILE ||
    241        aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
    242      RefPtr<File> file =
    243          ValueDeserializationHelper<StructuredCloneFile>::CreateUnwrappedFile(
    244              aCx, aDatabase, aFile, aData);
    245      return WrapAsJSObject(aCx, file, aResult);
    246    }
    247 
    248    const auto blob = ValueDeserializationHelper<StructuredCloneFile>::GetBlob(
    249        aCx, aDatabase, aFile);
    250    if (NS_WARN_IF(!blob)) {
    251      return false;
    252    }
    253 
    254    MOZ_ASSERT(aData.tag == SCTAG_DOM_BLOB);
    255    blob->Impl()->SetLazyData(VoidString(), aData.type, aData.size, INT64_MAX);
    256    MOZ_ASSERT(!blob->IsFile());
    257 
    258    // XXX The comment below is somewhat confusing, since it seems to imply
    259    // that this branch is only executed when called from ActorsParent, but
    260    // it's executed from both the parent and the child side code.
    261 
    262    // ActorsParent sends here a kind of half blob and half file wrapped into
    263    // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
    264    // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
    265    // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
    266    // File wrapping.
    267    // Before exposing it to content, we must recreate a DOM Blob object.
    268 
    269    const RefPtr<Blob> exposedBlob =
    270        Blob::Create(blob->GetParentObject(), blob->Impl());
    271    if (NS_WARN_IF(!exposedBlob)) {
    272      return false;
    273    }
    274 
    275    return WrapAsJSObject(aCx, exposedBlob, aResult);
    276  }
    277 };
    278 
    279 template <>
    280 class ValueDeserializationHelper<StructuredCloneFileParent>
    281    : public ValueDeserializationHelperBase {
    282 public:
    283  static bool CreateAndWrapMutableFile(JSContext* aCx,
    284                                       StructuredCloneFileParent& aFile,
    285                                       const MutableFileData& aData,
    286                                       JS::MutableHandle<JSObject*> aResult) {
    287    MOZ_ASSERT(aCx);
    288    MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
    289 
    290    // We are in an IDB SQLite schema upgrade where we don't care about a real
    291    // 'MutableFile', but we just care of having a proper |mType| flag.
    292 
    293    aFile.MutateType(StructuredCloneFileBase::eMutableFile);
    294 
    295    // Just make a dummy object.
    296    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
    297 
    298    if (NS_WARN_IF(!obj)) {
    299      return false;
    300    }
    301 
    302    aResult.set(obj);
    303    return true;
    304  }
    305 
    306  static RefPtr<Blob> GetBlob(JSContext* aCx, IDBDatabase* aDatabase,
    307                              const StructuredCloneFileParent& aFile) {
    308    // This is chrome code, so there is no parent, but still we want to set a
    309    // correct parent for the new File object.
    310    const auto global = [aDatabase, aCx]() -> nsCOMPtr<nsIGlobalObject> {
    311      if (NS_IsMainThread()) {
    312        if (aDatabase && aDatabase->GetParentObject()) {
    313          return aDatabase->GetParentObject();
    314        }
    315        return xpc::CurrentNativeGlobal(aCx);
    316      }
    317      const WorkerPrivate* const workerPrivate =
    318          GetCurrentThreadWorkerPrivate();
    319      MOZ_ASSERT(workerPrivate);
    320 
    321      WorkerGlobalScope* const globalScope = workerPrivate->GlobalScope();
    322      MOZ_ASSERT(globalScope);
    323 
    324      return do_QueryObject(globalScope);
    325    }();
    326 
    327    MOZ_ASSERT(global);
    328 
    329    // We do not have an mBlob but do have a DatabaseFileInfo.
    330    //
    331    // If we are creating an index, we do need a real-looking Blob/File instance
    332    // because the index's key path can reference their properties.  Rather than
    333    // create a fake-looking object, create a real Blob.
    334    //
    335    // If we are in a schema upgrade, we don't strictly need that, but we do not
    336    // need to optimize for that, and create it anyway.
    337    const nsCOMPtr<nsIFile> file = aFile.FileInfo().GetFileForFileInfo();
    338    if (!file) {
    339      return nullptr;
    340    }
    341 
    342    const auto impl = MakeRefPtr<FileBlobImpl>(file);
    343    impl->SetFileId(aFile.FileInfo().Id());
    344    return File::Create(global, impl);
    345  }
    346 };
    347 
    348 template <>
    349 class ValueDeserializationHelper<StructuredCloneFileChild>
    350    : public ValueDeserializationHelperBase {
    351 public:
    352  static bool CreateAndWrapMutableFile(JSContext* aCx,
    353                                       StructuredCloneFileChild& aFile,
    354                                       const MutableFileData& aData,
    355                                       JS::MutableHandle<JSObject*> aResult) {
    356    MOZ_ASSERT(aCx);
    357    MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eMutableFile);
    358 
    359    return false;
    360  }
    361 
    362  static RefPtr<Blob> GetBlob(JSContext* aCx, IDBDatabase* aDatabase,
    363                              const StructuredCloneFileChild& aFile) {
    364    if (aFile.HasBlob()) {
    365      return aFile.BlobPtr();
    366    }
    367 
    368    MOZ_CRASH("Expected a StructuredCloneFile with a Blob");
    369  }
    370 };
    371 
    372 }  // namespace
    373 
    374 template <typename StructuredCloneReadInfo>
    375 JSObject* CommonStructuredCloneReadCallback(
    376    JSContext* aCx, JSStructuredCloneReader* aReader,
    377    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
    378    StructuredCloneReadInfo* aCloneReadInfo, IDBDatabase* aDatabase) {
    379  // We need to statically assert that our tag values are what we expect
    380  // so that if people accidentally change them they notice.
    381  static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
    382                    SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
    383                    SCTAG_DOM_MUTABLEFILE == 0xffff8004 &&
    384                    SCTAG_DOM_FILE == 0xffff8005 &&
    385                    SCTAG_DOM_WASM_MODULE == 0xffff8006 &&
    386                    SCTAG_DOM_URLSEARCHPARAMS == 0xffff8014 &&
    387                    SCTAG_DOM_FILELIST == 0xffff8003,
    388                "You changed our structured clone tag values and just ate "
    389                "everyone's IndexedDB data.  I hope you are happy.");
    390 
    391  if (aTag == SCTAG_DOM_URLSEARCHPARAMS) {
    392    // Protect the result from a moving GC in ~RefPtr.
    393    JS::Rooted<JSObject*> result(aCx);
    394 
    395    {
    396      // Scope for the RefPtr below.
    397 
    398      nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
    399      if (!global) {
    400        return nullptr;
    401      }
    402 
    403      RefPtr<URLSearchParams> params = new URLSearchParams(global);
    404 
    405      uint32_t paramCount;
    406      uint32_t zero;
    407      if (!JS_ReadUint32Pair(aReader, &paramCount, &zero)) {
    408        return nullptr;
    409      }
    410 
    411      nsAutoString key;
    412      nsAutoString value;
    413      for (uint32_t index = 0; index < paramCount; index++) {
    414        if (!StructuredCloneHolder::ReadString(aReader, key) ||
    415            !StructuredCloneHolder::ReadString(aReader, value)) {
    416          return nullptr;
    417        }
    418        params->Append(NS_ConvertUTF16toUTF8(key),
    419                       NS_ConvertUTF16toUTF8(value));
    420      }
    421 
    422      if (!WrapAsJSObject(aCx, params, &result)) {
    423        return nullptr;
    424      }
    425    }
    426 
    427    return result;
    428  }
    429 
    430  using StructuredCloneFile =
    431      typename StructuredCloneReadInfo::StructuredCloneFile;
    432 
    433  if (aTag == SCTAG_DOM_FILELIST) {
    434    const auto& files = aCloneReadInfo->Files();
    435 
    436    uint32_t fileListLength = aData;
    437 
    438    if (fileListLength > files.Length()) {
    439      MOZ_ASSERT(false, "Bad file list length value!");
    440 
    441      return nullptr;
    442    }
    443 
    444    // We need to ensure that all RAII smart pointers which may trigger GC are
    445    // destroyed on return prior to this JS::Rooted being destroyed and
    446    // unrooting the pointer. This scope helps make this intent more explicit.
    447    JS::Rooted<JSObject*> obj(aCx);
    448    {
    449      nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx);
    450      if (!global) {
    451        MOZ_ASSERT(false, "Could not access global!");
    452 
    453        return nullptr;
    454      }
    455 
    456      RefPtr<FileList> fileList = new FileList(global);
    457 
    458      for (uint32_t i = 0u; i < fileListLength; ++i) {
    459        uint32_t tag = UINT32_MAX;
    460        uint32_t index = UINT32_MAX;
    461        if (!JS_ReadUint32Pair(aReader, &tag, &index)) {
    462          return nullptr;
    463        }
    464 
    465        if (tag != SCTAG_DOM_FILE) {
    466          MOZ_ASSERT(false, "Unexpected tag!");
    467 
    468          return nullptr;
    469        }
    470 
    471        if (uint64_t(index) >= files.Length()) {
    472          MOZ_ASSERT(false, "Bad index!");
    473 
    474          return nullptr;
    475        }
    476 
    477        BlobOrFileData data;
    478        if (NS_WARN_IF(!ReadBlobOrFile(aReader, tag, &data))) {
    479          return nullptr;
    480        }
    481 
    482        auto& fileObj = aCloneReadInfo->MutableFile(index);
    483 
    484        RefPtr<File> file = ValueDeserializationHelper<
    485            StructuredCloneFile>::CreateUnwrappedFile(aCx, aDatabase, fileObj,
    486                                                      data);
    487        if (!file) {
    488          MOZ_ASSERT(false, "Could not deserialize file!");
    489 
    490          return nullptr;
    491        }
    492 
    493        if (!fileList->Append(file)) {
    494          MOZ_ASSERT(false, "Could not extend filelist!");
    495 
    496          return nullptr;
    497        }
    498      }
    499 
    500      if (!WrapAsJSObject(aCx, fileList, &obj)) {
    501        return nullptr;
    502      }
    503    }
    504 
    505    return obj;
    506  }
    507 
    508  if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
    509      aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
    510      aTag == SCTAG_DOM_MUTABLEFILE || aTag == SCTAG_DOM_WASM_MODULE) {
    511    JS::Rooted<JSObject*> result(aCx);
    512 
    513    if (aTag == SCTAG_DOM_WASM_MODULE) {
    514      WasmModuleData data(aData);
    515      if (NS_WARN_IF(!ReadWasmModule(aReader, &data))) {
    516        return nullptr;
    517      }
    518 
    519      MOZ_ASSERT(data.compiledIndex == data.bytecodeIndex + 1);
    520      MOZ_ASSERT(!data.flags);
    521 
    522      const auto& files = aCloneReadInfo->Files();
    523      if (data.bytecodeIndex >= files.Length() ||
    524          data.compiledIndex >= files.Length()) {
    525        MOZ_ASSERT(false, "Bad index value!");
    526        return nullptr;
    527      }
    528 
    529      const auto& file = files[data.bytecodeIndex];
    530 
    531      if (NS_WARN_IF(!ValueDeserializationHelper<StructuredCloneFile>::
    532                         CreateAndWrapWasmModule(aCx, file, data, &result))) {
    533        return nullptr;
    534      }
    535 
    536      return result;
    537    }
    538 
    539    if (aData >= aCloneReadInfo->Files().Length()) {
    540      MOZ_ASSERT(false, "Bad index value!");
    541      return nullptr;
    542    }
    543 
    544    auto& file = aCloneReadInfo->MutableFile(aData);
    545 
    546    if (aTag == SCTAG_DOM_MUTABLEFILE) {
    547      MutableFileData data;
    548      if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {
    549        return nullptr;
    550      }
    551 
    552      if (NS_WARN_IF(!ValueDeserializationHelper<StructuredCloneFile>::
    553                         CreateAndWrapMutableFile(aCx, file, data, &result))) {
    554        return nullptr;
    555      }
    556 
    557      return result;
    558    }
    559 
    560    BlobOrFileData data;
    561    if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) {
    562      return nullptr;
    563    }
    564 
    565    if (NS_WARN_IF(!ValueDeserializationHelper<
    566                   StructuredCloneFile>::CreateAndWrapBlobOrFile(aCx, aDatabase,
    567                                                                 file, data,
    568                                                                 &result))) {
    569      return nullptr;
    570    }
    571 
    572    return result;
    573  }
    574 
    575  return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader, aTag,
    576                                                             true);
    577 }
    578 
    579 template JSObject* CommonStructuredCloneReadCallback(
    580    JSContext* aCx, JSStructuredCloneReader* aReader,
    581    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
    582    StructuredCloneReadInfoChild* aCloneReadInfo, IDBDatabase* aDatabase);
    583 
    584 template JSObject* CommonStructuredCloneReadCallback(
    585    JSContext* aCx, JSStructuredCloneReader* aReader,
    586    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
    587    StructuredCloneReadInfoParent* aCloneReadInfo, IDBDatabase* aDatabase);
    588 }  // namespace mozilla::dom::indexedDB