tor-browser

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

IDBObjectStore.cpp (63849B)


      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 "IDBObjectStore.h"
      8 
      9 #include <numeric>
     10 #include <utility>
     11 
     12 #include "IDBCursorType.h"
     13 #include "IDBDatabase.h"
     14 #include "IDBEvents.h"
     15 #include "IDBFactory.h"
     16 #include "IDBIndex.h"
     17 #include "IDBKeyRange.h"
     18 #include "IDBRequest.h"
     19 #include "IDBTransaction.h"
     20 #include "IndexedDBCommon.h"
     21 #include "IndexedDatabase.h"
     22 #include "IndexedDatabaseInlines.h"
     23 #include "IndexedDatabaseManager.h"
     24 #include "KeyPath.h"
     25 #include "ProfilerHelpers.h"
     26 #include "ReportInternalError.h"
     27 #include "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject
     28 #include "js/Class.h"
     29 #include "js/Date.h"
     30 #include "js/Object.h"  // JS::GetClass
     31 #include "js/PropertyAndElement.h"  // JS_GetProperty, JS_GetPropertyById, JS_HasOwnProperty, JS_HasOwnPropertyById
     32 #include "js/StructuredClone.h"
     33 #include "mozilla/EndianUtils.h"
     34 #include "mozilla/ErrorResult.h"
     35 #include "mozilla/dom/BindingUtils.h"
     36 #include "mozilla/dom/BlobBinding.h"
     37 #include "mozilla/dom/Document.h"
     38 #include "mozilla/dom/File.h"
     39 #include "mozilla/dom/FileList.h"
     40 #include "mozilla/dom/FileListBinding.h"
     41 #include "mozilla/dom/IDBObjectStoreBinding.h"
     42 #include "mozilla/dom/MemoryBlobImpl.h"
     43 #include "mozilla/dom/StreamBlobImpl.h"
     44 #include "mozilla/dom/StructuredCloneHolder.h"
     45 #include "mozilla/dom/StructuredCloneTags.h"
     46 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
     47 #include "mozilla/ipc/BackgroundChild.h"
     48 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     49 #include "nsCOMPtr.h"
     50 #include "nsIGlobalObject.h"
     51 #include "nsStreamUtils.h"
     52 #include "nsStringStream.h"
     53 
     54 // Include this last to avoid path problems on Windows.
     55 #include "ActorsChild.h"
     56 
     57 namespace mozilla::dom {
     58 
     59 using namespace mozilla::dom::indexedDB;
     60 using namespace mozilla::dom::quota;
     61 using namespace mozilla::ipc;
     62 
     63 namespace {
     64 
     65 Result<IndexUpdateInfo, nsresult> MakeIndexUpdateInfo(
     66    const int64_t aIndexID, const Key& aKey, const nsCString& aLocale) {
     67  IndexUpdateInfo indexUpdateInfo;
     68  indexUpdateInfo.indexId() = aIndexID;
     69  indexUpdateInfo.value() = aKey;
     70  if (!aLocale.IsEmpty()) {
     71    QM_TRY_UNWRAP(indexUpdateInfo.localizedValue(),
     72                  aKey.ToLocaleAwareKey(aLocale));
     73  }
     74  return indexUpdateInfo;
     75 }
     76 
     77 }  // namespace
     78 
     79 struct IDBObjectStore::StructuredCloneWriteInfo {
     80  JSAutoStructuredCloneBuffer mCloneBuffer;
     81  nsTArray<StructuredCloneFileChild> mFiles;
     82  IDBDatabase* mDatabase;
     83  uint64_t mOffsetToKeyProp;
     84 
     85  explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
     86      : mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB,
     87                     nullptr, nullptr),
     88        mDatabase(aDatabase),
     89        mOffsetToKeyProp(0) {
     90    MOZ_ASSERT(aDatabase);
     91 
     92    MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
     93  }
     94 
     95  StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo) noexcept
     96      : mCloneBuffer(std::move(aCloneWriteInfo.mCloneBuffer)),
     97        mFiles(std::move(aCloneWriteInfo.mFiles)),
     98        mDatabase(aCloneWriteInfo.mDatabase),
     99        mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp) {
    100    MOZ_ASSERT(mDatabase);
    101 
    102    MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
    103 
    104    aCloneWriteInfo.mOffsetToKeyProp = 0;
    105  }
    106 
    107  MOZ_COUNTED_DTOR(StructuredCloneWriteInfo)
    108 };
    109 
    110 // Used by ValueWrapper::Clone to hold strong references to any blob-like
    111 // objects through the clone process.  This is necessary because:
    112 // - The structured clone process may trigger content code via getters/other
    113 //   which can potentially cause existing strong references to be dropped,
    114 //   necessitating the clone to hold its own strong references.
    115 // - The structured clone can abort partway through, so it's necessary to track
    116 //   what strong references have been acquired so that they can be freed even
    117 //   if a de-serialization does not occur.
    118 struct IDBObjectStore::StructuredCloneInfo {
    119  nsTArray<StructuredCloneFileChild> mFiles;
    120 };
    121 
    122 namespace {
    123 
    124 struct MOZ_STACK_CLASS GetAddInfoClosure final {
    125  IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo;
    126  JS::Handle<JS::Value> mValue;
    127 
    128  GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo,
    129                    JS::Handle<JS::Value> aValue)
    130      : mCloneWriteInfo(aCloneWriteInfo), mValue(aValue) {
    131    MOZ_COUNT_CTOR(GetAddInfoClosure);
    132  }
    133 
    134  MOZ_COUNTED_DTOR(GetAddInfoClosure)
    135 };
    136 
    137 MovingNotNull<RefPtr<IDBRequest>> GenerateRequest(
    138    JSContext* aCx, IDBObjectStore* aObjectStore) {
    139  MOZ_ASSERT(aObjectStore);
    140  aObjectStore->AssertIsOnOwningThread();
    141 
    142  auto transaction = aObjectStore->AcquireTransaction();
    143  auto* const database = transaction->Database();
    144 
    145  return IDBRequest::Create(aCx, aObjectStore, database,
    146                            std::move(transaction));
    147 }
    148 
    149 bool WriteBlob(JSContext* aCx, JSStructuredCloneWriter* aWriter,
    150               Blob* const aBlob,
    151               IDBObjectStore::StructuredCloneWriteInfo* aCloneWriteInfo) {
    152  MOZ_ASSERT(aCx);
    153  MOZ_ASSERT(aWriter);
    154  MOZ_ASSERT(aBlob);
    155  MOZ_ASSERT(aCloneWriteInfo);
    156 
    157  ErrorResult rv;
    158  const uint64_t nativeEndianSize = aBlob->GetSize(rv);
    159  MOZ_ASSERT(!rv.Failed());
    160 
    161  const uint64_t size = NativeEndian::swapToLittleEndian(nativeEndianSize);
    162 
    163  nsString type;
    164  aBlob->GetType(type);
    165 
    166  const NS_ConvertUTF16toUTF8 convType(type);
    167  const uint32_t convTypeLength =
    168      NativeEndian::swapToLittleEndian(convType.Length());
    169 
    170  if (aCloneWriteInfo->mFiles.Length() > size_t(UINT32_MAX)) {
    171    MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
    172 
    173    return false;
    174  }
    175 
    176  const uint32_t index = aCloneWriteInfo->mFiles.Length();
    177 
    178  if (!JS_WriteUint32Pair(
    179          aWriter, aBlob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, index) ||
    180      !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
    181      !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
    182      !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
    183    return false;
    184  }
    185 
    186  const RefPtr<File> file = aBlob->ToFile();
    187  if (file) {
    188    ErrorResult rv;
    189    const int64_t nativeEndianLastModifiedDate = file->GetLastModified(rv);
    190    MOZ_ALWAYS_TRUE(!rv.Failed());
    191 
    192    const int64_t lastModifiedDate =
    193        NativeEndian::swapToLittleEndian(nativeEndianLastModifiedDate);
    194 
    195    nsString name;
    196    file->GetName(name);
    197 
    198    const NS_ConvertUTF16toUTF8 convName(name);
    199    const uint32_t convNameLength =
    200        NativeEndian::swapToLittleEndian(convName.Length());
    201 
    202    if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
    203        !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
    204        !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
    205      return false;
    206    }
    207  }
    208 
    209  aCloneWriteInfo->mFiles.EmplaceBack(StructuredCloneFileBase::eBlob, aBlob);
    210 
    211  return true;
    212 }
    213 
    214 bool StructuredCloneWriteCallback(JSContext* aCx,
    215                                  JSStructuredCloneWriter* aWriter,
    216                                  JS::Handle<JSObject*> aObj,
    217                                  bool* aSameProcessRequired, void* aClosure) {
    218  MOZ_ASSERT(aCx);
    219  MOZ_ASSERT(aWriter);
    220  MOZ_ASSERT(aClosure);
    221 
    222  auto* const cloneWriteInfo =
    223      static_cast<IDBObjectStore::StructuredCloneWriteInfo*>(aClosure);
    224 
    225  if (JS::GetClass(aObj) == IDBObjectStore::DummyPropClass()) {
    226    MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp);
    227    cloneWriteInfo->mOffsetToKeyProp = js::GetSCOffset(aWriter);
    228 
    229    uint64_t value = 0;
    230    // Omit endian swap
    231    return JS_WriteBytes(aWriter, &value, sizeof(value));
    232  }
    233 
    234  // UNWRAP_OBJECT calls might mutate this.
    235  JS::Rooted<JSObject*> obj(aCx, aObj);
    236 
    237  {
    238    FileList* fileList = nullptr;
    239    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) {
    240      const auto fileListStartIndex = cloneWriteInfo->mFiles.Length();
    241      const uint32_t fileListLength = fileList->Length();
    242 
    243      if (size_t(fileListStartIndex) > size_t(UINT32_MAX) - fileListLength) {
    244        MOZ_ASSERT(false,
    245                   "Fix the structured clone data to use a bigger type!");
    246        return false;
    247      }
    248 
    249      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, fileListLength)) {
    250        return false;
    251      }
    252 
    253      for (uint32_t i = 0; i < fileListLength; ++i) {
    254        File* file = fileList->Item(i);
    255        if (!WriteBlob(aCx, aWriter, file, cloneWriteInfo)) {
    256          return false;  // Everything should fail
    257        }
    258      }
    259 
    260      return true;
    261    }
    262  }
    263 
    264  {
    265    Blob* blob = nullptr;
    266    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
    267      return WriteBlob(aCx, aWriter, blob, cloneWriteInfo);
    268    }
    269  }
    270 
    271  return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter,
    272                                                              aObj);
    273 }
    274 
    275 bool CopyingWriteBlob(JSContext* aCx, JSStructuredCloneWriter* aWriter,
    276                      Blob* const aBlob,
    277                      IDBObjectStore::StructuredCloneInfo* aCloneInfo) {
    278  MOZ_ASSERT(aCx);
    279  MOZ_ASSERT(aWriter);
    280  MOZ_ASSERT(aBlob);
    281  MOZ_ASSERT(aCloneInfo);
    282 
    283  if (aCloneInfo->mFiles.Length() > size_t(UINT32_MAX)) {
    284    MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
    285    return false;
    286  }
    287 
    288  const uint32_t index = aCloneInfo->mFiles.Length();
    289 
    290  if (!JS_WriteUint32Pair(
    291          aWriter, aBlob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, index)) {
    292    return false;
    293  }
    294 
    295  aCloneInfo->mFiles.EmplaceBack(StructuredCloneFileBase::eBlob, aBlob);
    296 
    297  return true;
    298 }
    299 
    300 bool CopyingStructuredCloneWriteCallback(JSContext* aCx,
    301                                         JSStructuredCloneWriter* aWriter,
    302                                         JS::Handle<JSObject*> aObj,
    303                                         bool* aSameProcessRequired,
    304                                         void* aClosure) {
    305  MOZ_ASSERT(aCx);
    306  MOZ_ASSERT(aWriter);
    307  MOZ_ASSERT(aClosure);
    308 
    309  auto* const cloneInfo =
    310      static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
    311 
    312  // UNWRAP_OBJECT calls might mutate this.
    313  JS::Rooted<JSObject*> obj(aCx, aObj);
    314 
    315  {
    316    FileList* fileList = nullptr;
    317    if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, &obj, fileList))) {
    318      const auto fileListStartIndex = cloneInfo->mFiles.Length();
    319      const uint32_t fileListLength = fileList->Length();
    320 
    321      if (size_t(fileListStartIndex) > size_t(UINT32_MAX) - fileListLength) {
    322        MOZ_ASSERT(false,
    323                   "Fix the structured clone data to use a bigger type!");
    324        return false;
    325      }
    326 
    327      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, fileListLength)) {
    328        return false;
    329      }
    330 
    331      for (uint32_t i = 0; i < fileList->Length(); ++i) {
    332        File* file = fileList->Item(i);
    333        if (!CopyingWriteBlob(aCx, aWriter, file, cloneInfo)) {
    334          return false;  // Everything should fail
    335        }
    336      }
    337 
    338      return true;
    339    }
    340  }
    341 
    342  {
    343    Blob* blob = nullptr;
    344    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
    345      return CopyingWriteBlob(aCx, aWriter, blob, cloneInfo);
    346    }
    347  }
    348 
    349  return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter,
    350                                                              aObj);
    351 }
    352 
    353 void StructuredCloneErrorCallback(JSContext* aCx, uint32_t aErrorId,
    354                                  void* aClosure, const char* aErrorMessage) {
    355  // This callback is only used to prevent the default cloning TypeErrors
    356  // from being thrown, as we will throw DataCloneErrors instead per spec.
    357 }
    358 
    359 nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) {
    360  static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
    361      nullptr /* read */,
    362      StructuredCloneWriteCallback /* write */,
    363      StructuredCloneErrorCallback /* reportError */,
    364      nullptr /* readTransfer */,
    365      nullptr /* writeTransfer */,
    366      nullptr /* freeTransfer */,
    367      nullptr /* canTransfer */,
    368      nullptr /* sabCloned */
    369  };
    370 
    371  MOZ_ASSERT(aCx);
    372 
    373  auto* const data = static_cast<GetAddInfoClosure*>(aClosure);
    374  MOZ_ASSERT(data);
    375 
    376  data->mCloneWriteInfo.mOffsetToKeyProp = 0;
    377 
    378  if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx, data->mValue,
    379                                                &kStructuredCloneCallbacks,
    380                                                &data->mCloneWriteInfo)) {
    381    return NS_ERROR_DOM_DATA_CLONE_ERR;
    382  }
    383 
    384  return NS_OK;
    385 }
    386 
    387 using indexedDB::WrapAsJSObject;
    388 
    389 template <typename T>
    390 JSObject* WrapAsJSObject(JSContext* const aCx, T& aBaseObject) {
    391  JS::Rooted<JSObject*> result(aCx);
    392  const bool res = WrapAsJSObject(aCx, aBaseObject, &result);
    393  return res ? static_cast<JSObject*>(result) : nullptr;
    394 }
    395 
    396 JSObject* CopyingStructuredCloneReadCallback(
    397    JSContext* aCx, JSStructuredCloneReader* aReader,
    398    const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
    399    void* aClosure) {
    400  MOZ_ASSERT(aTag != SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
    401 
    402  if (aTag == SCTAG_DOM_FILELIST) {
    403    auto* const cloneInfo =
    404        static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
    405 
    406    // For empty filelist, aData is not used but must remain within bounds.
    407    const auto& files = cloneInfo->mFiles;
    408    const uint32_t fileListLength = aData;
    409 
    410    if (fileListLength > files.Length()) {
    411      MOZ_ASSERT(false, "Bad file list length value!");
    412 
    413      return nullptr;
    414    }
    415 
    416    // We need to ensure that all RAII smart pointers which may trigger GC are
    417    // destroyed on return prior to this JS::Rooted being destroyed and
    418    // unrooting the pointer. This scope helps make this intent more explicit.
    419    JS::Rooted<JSObject*> obj(aCx);
    420    {
    421      nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx);
    422      if (!global) {
    423        MOZ_ASSERT(false, "Could not access global!");
    424 
    425        return nullptr;
    426      }
    427 
    428      RefPtr<FileList> fileList = new FileList(global);
    429 
    430      for (uint32_t i = 0u; i < fileListLength; ++i) {
    431        uint32_t tag = UINT32_MAX;
    432        uint32_t index = UINT32_MAX;
    433        if (!JS_ReadUint32Pair(aReader, &tag, &index)) {
    434          return nullptr;
    435        }
    436 
    437        const bool hasFileTag = tag == SCTAG_DOM_FILE;
    438        if (!hasFileTag) {
    439          MOZ_ASSERT(false, "Unexpected tag!");
    440 
    441          return nullptr;
    442        }
    443 
    444        if (uint64_t(index) >= cloneInfo->mFiles.Length()) {
    445          MOZ_ASSERT(false, "Bad index!");
    446 
    447          return nullptr;
    448        }
    449 
    450        StructuredCloneFileChild& fileChild = cloneInfo->mFiles[index];
    451        MOZ_ASSERT(fileChild.Type() == StructuredCloneFileBase::eBlob);
    452 
    453        RefPtr<Blob> blob = fileChild.BlobPtr();
    454        MOZ_ASSERT(blob->IsFile());
    455 
    456        RefPtr<File> file = blob->ToFile();
    457        if (!file) {
    458          MOZ_ASSERT(false, "Could not convert blob to file!");
    459 
    460          return nullptr;
    461        }
    462 
    463        if (!fileList->Append(file)) {
    464          MOZ_ASSERT(false, "Could not extend filelist!");
    465 
    466          return nullptr;
    467        }
    468      }
    469 
    470      if (!WrapAsJSObject(aCx, fileList, &obj)) {
    471        return nullptr;
    472      }
    473    }
    474 
    475    return obj;
    476  }
    477 
    478  if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
    479      aTag == SCTAG_DOM_MUTABLEFILE) {
    480    auto* const cloneInfo =
    481        static_cast<IDBObjectStore::StructuredCloneInfo*>(aClosure);
    482 
    483    if (aData >= cloneInfo->mFiles.Length()) {
    484      MOZ_ASSERT(false, "Bad index value!");
    485      return nullptr;
    486    }
    487 
    488    StructuredCloneFileChild& file = cloneInfo->mFiles[aData];
    489 
    490    switch (static_cast<StructuredCloneTags>(aTag)) {
    491      case SCTAG_DOM_BLOB:
    492        MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eBlob);
    493        MOZ_ASSERT(!file.Blob().IsFile());
    494 
    495        return WrapAsJSObject(aCx, file.MutableBlob());
    496 
    497      case SCTAG_DOM_FILE: {
    498        MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eBlob);
    499 
    500        JS::Rooted<JSObject*> result(aCx);
    501 
    502        {
    503          // Create a scope so ~RefPtr fires before returning an unwrapped
    504          // JS::Value.
    505          const RefPtr<Blob> blob = file.BlobPtr();
    506          MOZ_ASSERT(blob->IsFile());
    507 
    508          const RefPtr<File> file = blob->ToFile();
    509          MOZ_ASSERT(file);
    510 
    511          if (!WrapAsJSObject(aCx, file, &result)) {
    512            return nullptr;
    513          }
    514        }
    515 
    516        return result;
    517      }
    518 
    519      case SCTAG_DOM_MUTABLEFILE:
    520        MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eMutableFile);
    521 
    522        return nullptr;
    523 
    524      default:
    525        // This cannot be reached due to the if condition before.
    526        break;
    527    }
    528  }
    529 
    530  return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader, aTag,
    531                                                             true);
    532 }
    533 
    534 }  // namespace
    535 
    536 const JSClass IDBObjectStore::sDummyPropJSClass = {
    537    "IDBObjectStore Dummy", 0 /* flags */
    538 };
    539 
    540 IDBObjectStore::IDBObjectStore(SafeRefPtr<IDBTransaction> aTransaction,
    541                               ObjectStoreSpec* aSpec)
    542    : mTransaction(std::move(aTransaction)),
    543      mCachedKeyPath(JS::UndefinedValue()),
    544      mSpec(aSpec),
    545      mId(aSpec->metadata().id()),
    546      mRooted(false) {
    547  MOZ_ASSERT(mTransaction);
    548  mTransaction->AssertIsOnOwningThread();
    549  MOZ_ASSERT(aSpec);
    550 }
    551 
    552 IDBObjectStore::~IDBObjectStore() {
    553  AssertIsOnOwningThread();
    554 
    555  if (mRooted) {
    556    mozilla::DropJSObjects(this);
    557  }
    558 }
    559 
    560 // static
    561 RefPtr<IDBObjectStore> IDBObjectStore::Create(
    562    SafeRefPtr<IDBTransaction> aTransaction, ObjectStoreSpec& aSpec) {
    563  MOZ_ASSERT(aTransaction);
    564  aTransaction->AssertIsOnOwningThread();
    565 
    566  return new IDBObjectStore(std::move(aTransaction), &aSpec);
    567 }
    568 
    569 // static
    570 void IDBObjectStore::AppendIndexUpdateInfo(
    571    const int64_t aIndexID, const KeyPath& aKeyPath, const bool aMultiEntry,
    572    const nsCString& aLocale, JSContext* const aCx, JS::Handle<JS::Value> aVal,
    573    nsTArray<IndexUpdateInfo>* const aUpdateInfoArray,
    574    const VoidOrObjectStoreKeyPathString& aAutoIncrementedObjectStoreKeyPath,
    575    ErrorResult* const aRv) {
    576  // This precondition holds when `aVal` is the result of a structured clone.
    577  js::AutoAssertNoContentJS noContentJS(aCx);
    578 
    579  static_assert(std::is_same_v<IDBObjectStore::VoidOrObjectStoreKeyPathString,
    580                               KeyPath::VoidOrObjectStoreKeyPathString>,
    581                "Inconsistent types");
    582 
    583  if (!aMultiEntry) {
    584    Key key;
    585    *aRv =
    586        aKeyPath.ExtractKey(aCx, aVal, key, aAutoIncrementedObjectStoreKeyPath);
    587 
    588    // If an index's keyPath doesn't match an object, we ignore that object.
    589    if (aRv->ErrorCodeIs(NS_ERROR_DOM_INDEXEDDB_DATA_ERR) || key.IsUnset()) {
    590      aRv->SuppressException();
    591      return;
    592    }
    593 
    594    if (aRv->Failed()) {
    595      return;
    596    }
    597 
    598    QM_TRY_UNWRAP(auto item, MakeIndexUpdateInfo(aIndexID, key, aLocale),
    599                  QM_VOID,
    600                  [aRv](const nsresult tryResult) { aRv->Throw(tryResult); });
    601 
    602    aUpdateInfoArray->AppendElement(std::move(item));
    603    return;
    604  }
    605 
    606  JS::Rooted<JS::Value> val(aCx);
    607  if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
    608    return;
    609  }
    610 
    611  bool isArray;
    612  if (NS_WARN_IF(!JS::IsArrayObject(aCx, val, &isArray))) {
    613    IDB_REPORT_INTERNAL_ERR();
    614    aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    615    return;
    616  }
    617  if (isArray) {
    618    JS::Rooted<JSObject*> array(aCx, &val.toObject());
    619    uint32_t arrayLength;
    620    if (NS_WARN_IF(!JS::GetArrayLength(aCx, array, &arrayLength))) {
    621      IDB_REPORT_INTERNAL_ERR();
    622      aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    623      return;
    624    }
    625 
    626    for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
    627      JS::Rooted<JS::PropertyKey> indexId(aCx);
    628      if (NS_WARN_IF(!JS_IndexToId(aCx, arrayIndex, &indexId))) {
    629        IDB_REPORT_INTERNAL_ERR();
    630        aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    631        return;
    632      }
    633 
    634      bool hasOwnProperty;
    635      if (NS_WARN_IF(
    636              !JS_HasOwnPropertyById(aCx, array, indexId, &hasOwnProperty))) {
    637        IDB_REPORT_INTERNAL_ERR();
    638        aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    639        return;
    640      }
    641 
    642      if (!hasOwnProperty) {
    643        continue;
    644      }
    645 
    646      JS::Rooted<JS::Value> arrayItem(aCx);
    647      if (NS_WARN_IF(!JS_GetPropertyById(aCx, array, indexId, &arrayItem))) {
    648        IDB_REPORT_INTERNAL_ERR();
    649        aRv->Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    650        return;
    651      }
    652 
    653      Key value;
    654      auto result = value.SetFromJSVal(aCx, arrayItem);
    655      if (result.isErr() || value.IsUnset()) {
    656        // Not a value we can do anything with, ignore it.
    657        if (result.isErr() &&
    658            result.inspectErr().Is(SpecialValues::Exception)) {
    659          result.unwrapErr().AsException().SuppressException();
    660        }
    661        continue;
    662      }
    663 
    664      QM_TRY_UNWRAP(auto item, MakeIndexUpdateInfo(aIndexID, value, aLocale),
    665                    QM_VOID,
    666                    [aRv](const nsresult tryResult) { aRv->Throw(tryResult); });
    667 
    668      aUpdateInfoArray->AppendElement(std::move(item));
    669    }
    670  } else {
    671    Key value;
    672    auto result = value.SetFromJSVal(aCx, val);
    673    if (result.isErr() || value.IsUnset()) {
    674      // Not a value we can do anything with, ignore it.
    675      if (result.isErr() && result.inspectErr().Is(SpecialValues::Exception)) {
    676        result.unwrapErr().AsException().SuppressException();
    677      }
    678      return;
    679    }
    680 
    681    QM_TRY_UNWRAP(auto item, MakeIndexUpdateInfo(aIndexID, value, aLocale),
    682                  QM_VOID,
    683                  [aRv](const nsresult tryResult) { aRv->Throw(tryResult); });
    684 
    685    aUpdateInfoArray->AppendElement(std::move(item));
    686  }
    687 }
    688 
    689 // static
    690 void IDBObjectStore::ClearCloneReadInfo(
    691    StructuredCloneReadInfoChild& aReadInfo) {
    692  // This is kind of tricky, we only want to release stuff on the main thread,
    693  // but we can end up being called on other threads if we have already been
    694  // cleared on the main thread.
    695  if (!aReadInfo.HasFiles()) {
    696    return;
    697  }
    698 
    699  aReadInfo.ReleaseFiles();
    700 }
    701 
    702 // static
    703 bool IDBObjectStore::DeserializeValue(
    704    JSContext* aCx, StructuredCloneReadInfoChild&& aCloneReadInfo,
    705    JS::MutableHandle<JS::Value> aValue) {
    706  MOZ_ASSERT(aCx);
    707 
    708  if (!aCloneReadInfo.Data().Size()) {
    709    aValue.setUndefined();
    710    return true;
    711  }
    712 
    713  MOZ_ASSERT(!(aCloneReadInfo.Data().Size() % sizeof(uint64_t)));
    714 
    715  static const JSStructuredCloneCallbacks callbacks = {
    716      StructuredCloneReadCallback<StructuredCloneReadInfoChild>,
    717      nullptr,
    718      StructuredCloneErrorCallback,
    719      nullptr,
    720      nullptr,
    721      nullptr,
    722      nullptr,
    723      nullptr};
    724 
    725  // FIXME: Consider to use StructuredCloneHolder here and in other
    726  //        deserializing methods.
    727  return JS_ReadStructuredClone(
    728      aCx, aCloneReadInfo.Data(), JS_STRUCTURED_CLONE_VERSION,
    729      JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
    730      JS::CloneDataPolicy(), &callbacks, &aCloneReadInfo);
    731 }
    732 
    733 #ifdef DEBUG
    734 
    735 void IDBObjectStore::AssertIsOnOwningThread() const {
    736  MOZ_ASSERT(mTransaction);
    737  mTransaction->AssertIsOnOwningThread();
    738 }
    739 
    740 #endif  // DEBUG
    741 
    742 void IDBObjectStore::GetAddInfo(JSContext* aCx, ValueWrapper& aValueWrapper,
    743                                JS::Handle<JS::Value> aKeyVal,
    744                                StructuredCloneWriteInfo& aCloneWriteInfo,
    745                                Key& aKey,
    746                                nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
    747                                ErrorResult& aRv) {
    748  // Return DATA_ERR if a key was passed in and this objectStore uses inline
    749  // keys.
    750  if (!aKeyVal.isUndefined() && HasValidKeyPath()) {
    751    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    752    return;
    753  }
    754 
    755  const bool isAutoIncrement = AutoIncrement();
    756 
    757  if (!HasValidKeyPath()) {
    758    // Out-of-line keys must be passed in.
    759    auto result = aKey.SetFromJSVal(aCx, aKeyVal);
    760    if (result.isErr()) {
    761      aRv = result.unwrapErr().ExtractErrorResult(
    762          InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
    763      return;
    764    }
    765  } else if (!isAutoIncrement) {
    766    if (!aValueWrapper.Clone(aCx)) {
    767      aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
    768      return;
    769    }
    770 
    771    aRv = GetKeyPath().ExtractKey(aCx, aValueWrapper.Value(), aKey);
    772    if (aRv.Failed()) {
    773      return;
    774    }
    775  }
    776 
    777  // Return DATA_ERR if no key was specified this isn't an autoIncrement
    778  // objectStore.
    779  if (aKey.IsUnset() && !isAutoIncrement) {
    780    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    781    return;
    782  }
    783 
    784  // Figure out indexes and the index values to update here.
    785 
    786  if (mSpec->indexes().Length() && !aValueWrapper.Clone(aCx)) {
    787    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
    788    return;
    789  }
    790 
    791  {
    792    const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
    793    const uint32_t idxCount = indexes.Length();
    794 
    795    const auto& autoIncrementedObjectStoreKeyPath =
    796        [this]() -> const nsAString& {
    797      if (AutoIncrement() && GetKeyPath().IsValid()) {
    798        // By https://w3c.github.io/IndexedDB/#database-interface ,
    799        // createObjectStore algorithm, step 8, neither arrays nor empty paths
    800        // are allowed for autoincremented object stores.
    801        // See also KeyPath::IsAllowedForObjectStore.
    802        MOZ_ASSERT(GetKeyPath().IsString());
    803        MOZ_ASSERT(!GetKeyPath().IsEmpty());
    804        return GetKeyPath().mStrings[0];
    805      }
    806 
    807      return VoidString();
    808    }();
    809 
    810    aUpdateInfoArray.SetCapacity(idxCount);  // Pretty good estimate
    811 
    812    for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) {
    813      const IndexMetadata& metadata = indexes[idxIndex];
    814 
    815      AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(),
    816                            metadata.multiEntry(), metadata.locale(), aCx,
    817                            aValueWrapper.Value(), &aUpdateInfoArray,
    818                            autoIncrementedObjectStoreKeyPath, &aRv);
    819      if (NS_WARN_IF(aRv.Failed())) {
    820        return;
    821      }
    822    }
    823  }
    824 
    825  if (isAutoIncrement && HasValidKeyPath()) {
    826    if (!aValueWrapper.Clone(aCx)) {
    827      aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
    828      return;
    829    }
    830 
    831    GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
    832 
    833    MOZ_ASSERT(aKey.IsUnset());
    834 
    835    aRv = GetKeyPath().ExtractOrCreateKey(aCx, aValueWrapper.Value(), aKey,
    836                                          &GetAddInfoCallback, &data);
    837  } else {
    838    GetAddInfoClosure data(aCloneWriteInfo, aValueWrapper.Value());
    839 
    840    aRv = GetAddInfoCallback(aCx, &data);
    841  }
    842 }
    843 
    844 RefPtr<IDBRequest> IDBObjectStore::AddOrPut(JSContext* aCx,
    845                                            ValueWrapper& aValueWrapper,
    846                                            JS::Handle<JS::Value> aKey,
    847                                            bool aOverwrite, bool aFromCursor,
    848                                            ErrorResult& aRv) {
    849  AssertIsOnOwningThread();
    850  MOZ_ASSERT(aCx);
    851  MOZ_ASSERT_IF(aFromCursor, aOverwrite);
    852 
    853  if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
    854      mDeletedSpec) {
    855    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    856    return nullptr;
    857  }
    858 
    859  if (!mTransaction->IsActive()) {
    860    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    861    return nullptr;
    862  }
    863 
    864  if (!mTransaction->IsWriteAllowed()) {
    865    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
    866    return nullptr;
    867  }
    868 
    869  Key key;
    870  StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database());
    871  nsTArray<IndexUpdateInfo> updateInfos;
    872 
    873  // According to spec https://w3c.github.io/IndexedDB/#clone-value,
    874  // the transaction must be in inactive state during clone
    875  mTransaction->TransitionToInactive();
    876 
    877 #ifdef DEBUG
    878  const uint32_t previousPendingRequestCount{
    879      mTransaction->GetPendingRequestCount()};
    880 #endif
    881  GetAddInfo(aCx, aValueWrapper, aKey, cloneWriteInfo, key, updateInfos, aRv);
    882  // Check that new requests were rejected in the Inactive state
    883  // and possibly in the Finished state, if the transaction has been aborted,
    884  // during the structured cloning.
    885  MOZ_ASSERT(mTransaction->GetPendingRequestCount() ==
    886             previousPendingRequestCount);
    887 
    888  if (!mTransaction->IsAborted()) {
    889    mTransaction->TransitionToActive();
    890  } else if (!aRv.Failed()) {
    891    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
    892    return nullptr;  // It is mandatory to return right after throw
    893  }
    894 
    895  if (aRv.Failed()) {
    896    return nullptr;
    897  }
    898 
    899  // Total structured clone size in bytes.
    900  const size_t structuredCloneSize = cloneWriteInfo.mCloneBuffer.data().Size();
    901 
    902  // Bug 1783242 introduced the use of shared memory for serializing large
    903  // JSStructuredCloneData, which required adjusting IPC message size estimation
    904  // calculations below. However, structured clone sizes are still limited:
    905  // - On 32-bit platforms, crashes can occur around 2GB, and on 64-bit
    906  //   platforms around 4GB, even with shared memory.
    907  // - Ideally, the content process would write directly to a file to avoid
    908  //   shared memory and data copying (see Bug 1944231).
    909  //
    910  // For now, structured clone sizes are capped at < 1042 MB (configurable via a
    911  // preference).
    912  if (structuredCloneSize > IndexedDatabaseManager::MaxStructuredCloneSize()) {
    913    IDB_REPORT_INTERNAL_ERR();
    914    aRv.ThrowUnknownError(nsPrintfCString(
    915        "The structured clone is too large"
    916        " (size=%zu bytes, max=%u bytes).",
    917        structuredCloneSize, IndexedDatabaseManager::MaxStructuredCloneSize()));
    918    return nullptr;
    919  }
    920 
    921  // Check the size limit of the serialized message which mainly consists of
    922  // a StructuredCloneBuffer, an encoded object key, and the encoded index keys.
    923  // kMaxIDBMsgOverhead covers the minor stuff not included in this calculation
    924  // because the precise calculation would slow down this AddOrPut operation.
    925  static const size_t kMaxIDBMsgOverhead = 1024 * 1024;  // 1MB
    926  const uint32_t maximalSizeFromPref =
    927      IndexedDatabaseManager::MaxSerializedMsgSize();
    928  MOZ_ASSERT(maximalSizeFromPref > kMaxIDBMsgOverhead);
    929  const size_t kMaxMessageSize = maximalSizeFromPref - kMaxIDBMsgOverhead;
    930 
    931  // Serialized structured clone size in bytes. For structured clone sizes >
    932  // IPC::kMessageBufferShmemThreshold, only the size and shared memory handle
    933  // are included in the IPC message. The value 16 is an estimate.
    934  const size_t serializedStructuredCloneSize =
    935      structuredCloneSize > IPC::kMessageBufferShmemThreshold
    936          ? 16
    937          : structuredCloneSize;
    938 
    939  const size_t indexUpdateInfoSize =
    940      std::accumulate(updateInfos.cbegin(), updateInfos.cend(), 0u,
    941                      [](size_t old, const IndexUpdateInfo& updateInfo) {
    942                        return old + updateInfo.value().GetBuffer().Length() +
    943                               updateInfo.localizedValue().GetBuffer().Length();
    944                      });
    945 
    946  // TODO: Adjust the calculation of messageSize to account for the fallback
    947  // to shared memory during serialization of the primary key and index keys if
    948  // their size exceeds IPC::kMessageBufferShmemThreshold. This ensures the
    949  // calculated size accurately reflects the actual IPC message size.
    950  // See also bug 1945043.
    951  const size_t messageSize = serializedStructuredCloneSize +
    952                             key.GetBuffer().Length() + indexUpdateInfoSize;
    953 
    954  if (messageSize > kMaxMessageSize) {
    955    IDB_REPORT_INTERNAL_ERR();
    956    aRv.ThrowUnknownError(
    957        nsPrintfCString("The serialized value is too large"
    958                        " (size=%zu bytes, max=%zu bytes).",
    959                        messageSize, kMaxMessageSize));
    960    return nullptr;
    961  }
    962 
    963  ObjectStoreAddPutParams commonParams;
    964  commonParams.objectStoreId() = Id();
    965  commonParams.cloneInfo().data().data =
    966      std::move(cloneWriteInfo.mCloneBuffer.data());
    967  commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
    968  commonParams.key() = key;
    969  commonParams.indexUpdateInfos() = std::move(updateInfos);
    970 
    971  // Convert any blobs or mutable files into FileAddInfos.
    972  QM_TRY_UNWRAP(
    973      commonParams.fileAddInfos(),
    974      TransformIntoNewArrayAbortOnErr(
    975          cloneWriteInfo.mFiles,
    976          [&database = *mTransaction->Database()](
    977              auto& file) -> Result<FileAddInfo, nsresult> {
    978            switch (file.Type()) {
    979              case StructuredCloneFileBase::eBlob: {
    980                MOZ_ASSERT(file.HasBlob());
    981 
    982                PBackgroundIDBDatabaseFileChild* const fileActor =
    983                    database.GetOrCreateFileActorForBlob(file.MutableBlob());
    984                if (NS_WARN_IF(!fileActor)) {
    985                  IDB_REPORT_INTERNAL_ERR();
    986                  return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    987                }
    988 
    989                return FileAddInfo{WrapNotNull(fileActor),
    990                                   StructuredCloneFileBase::eBlob};
    991              }
    992 
    993              case StructuredCloneFileBase::eWasmBytecode:
    994              case StructuredCloneFileBase::eWasmCompiled: {
    995                MOZ_ASSERT(file.HasBlob());
    996 
    997                PBackgroundIDBDatabaseFileChild* const fileActor =
    998                    database.GetOrCreateFileActorForBlob(file.MutableBlob());
    999                if (NS_WARN_IF(!fileActor)) {
   1000                  IDB_REPORT_INTERNAL_ERR();
   1001                  return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   1002                }
   1003 
   1004                return FileAddInfo{WrapNotNull(fileActor), file.Type()};
   1005              }
   1006 
   1007              default:
   1008                MOZ_CRASH("Should never get here!");
   1009            }
   1010          },
   1011          fallible),
   1012      nullptr, [&aRv](const nsresult result) { aRv = result; });
   1013 
   1014  const auto& params =
   1015      aOverwrite ? RequestParams{ObjectStorePutParams(std::move(commonParams))}
   1016                 : RequestParams{ObjectStoreAddParams(std::move(commonParams))};
   1017 
   1018  auto request = GenerateRequest(aCx, this).unwrap();
   1019 
   1020  if (!aFromCursor) {
   1021    if (aOverwrite) {
   1022      IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1023          "database(%s).transaction(%s).objectStore(%s).put(%s)",
   1024          "IDBObjectStore.put(%.0s%.0s%.0s%.0s)",
   1025          mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1026          IDB_LOG_STRINGIFY(mTransaction->Database()),
   1027          IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1028          IDB_LOG_STRINGIFY(key));
   1029    } else {
   1030      IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1031          "database(%s).transaction(%s).objectStore(%s).add(%s)",
   1032          "IDBObjectStore.add(%.0s%.0s%.0s%.0s)",
   1033          mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1034          IDB_LOG_STRINGIFY(mTransaction->Database()),
   1035          IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1036          IDB_LOG_STRINGIFY(key));
   1037    }
   1038  }
   1039 
   1040  mTransaction->StartRequest(request, params);
   1041 
   1042  mTransaction->InvalidateCursorCaches();
   1043 
   1044  return request;
   1045 }
   1046 
   1047 RefPtr<IDBRequest> IDBObjectStore::GetAllInternal(
   1048    bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aKey,
   1049    const Optional<uint32_t>& aLimit, ErrorResult& aRv) {
   1050  AssertIsOnOwningThread();
   1051 
   1052  if (mDeletedSpec) {
   1053    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1054    return nullptr;
   1055  }
   1056 
   1057  if (!mTransaction->IsActive()) {
   1058    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1059    return nullptr;
   1060  }
   1061 
   1062  RefPtr<IDBKeyRange> keyRange;
   1063  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
   1064  if (NS_WARN_IF(aRv.Failed())) {
   1065    return nullptr;
   1066  }
   1067 
   1068  const int64_t id = Id();
   1069 
   1070  Maybe<SerializedKeyRange> optionalKeyRange;
   1071  if (keyRange) {
   1072    SerializedKeyRange serializedKeyRange;
   1073    keyRange->ToSerialized(serializedKeyRange);
   1074    optionalKeyRange.emplace(serializedKeyRange);
   1075  }
   1076 
   1077  const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
   1078 
   1079  RequestParams params;
   1080  if (aKeysOnly) {
   1081    params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit);
   1082  } else {
   1083    params = ObjectStoreGetAllParams(id, optionalKeyRange, limit);
   1084  }
   1085 
   1086  auto request = GenerateRequest(aCx, this).unwrap();
   1087 
   1088  if (aKeysOnly) {
   1089    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1090        "database(%s).transaction(%s).objectStore(%s)."
   1091        "getAllKeys(%s, %s)",
   1092        "IDBObjectStore.getAllKeys(%.0s%.0s%.0s%.0s%.0s)",
   1093        mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1094        IDB_LOG_STRINGIFY(mTransaction->Database()),
   1095        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1096        IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit));
   1097  } else {
   1098    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1099        "database(%s).transaction(%s).objectStore(%s)."
   1100        "getAll(%s, %s)",
   1101        "IDBObjectStore.getAll(%.0s%.0s%.0s%.0s%.0s)",
   1102        mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1103        IDB_LOG_STRINGIFY(mTransaction->Database()),
   1104        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1105        IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aLimit));
   1106  }
   1107 
   1108  // TODO: This is necessary to preserve request ordering only. Proper
   1109  // sequencing of requests should be done in a more sophisticated manner that
   1110  // doesn't require invalidating cursor caches (Bug 1580499).
   1111  mTransaction->InvalidateCursorCaches();
   1112 
   1113  mTransaction->StartRequest(request, params);
   1114 
   1115  return request;
   1116 }
   1117 
   1118 RefPtr<IDBRequest> IDBObjectStore::Add(JSContext* aCx,
   1119                                       JS::Handle<JS::Value> aValue,
   1120                                       JS::Handle<JS::Value> aKey,
   1121                                       ErrorResult& aRv) {
   1122  AssertIsOnOwningThread();
   1123 
   1124  ValueWrapper valueWrapper(aCx, aValue);
   1125 
   1126  return AddOrPut(aCx, valueWrapper, aKey, false, /* aFromCursor */ false, aRv);
   1127 }
   1128 
   1129 RefPtr<IDBRequest> IDBObjectStore::Put(JSContext* aCx,
   1130                                       JS::Handle<JS::Value> aValue,
   1131                                       JS::Handle<JS::Value> aKey,
   1132                                       ErrorResult& aRv) {
   1133  AssertIsOnOwningThread();
   1134 
   1135  ValueWrapper valueWrapper(aCx, aValue);
   1136 
   1137  return AddOrPut(aCx, valueWrapper, aKey, true, /* aFromCursor */ false, aRv);
   1138 }
   1139 
   1140 RefPtr<IDBRequest> IDBObjectStore::Delete(JSContext* aCx,
   1141                                          JS::Handle<JS::Value> aKey,
   1142                                          ErrorResult& aRv) {
   1143  AssertIsOnOwningThread();
   1144 
   1145  return DeleteInternal(aCx, aKey, /* aFromCursor */ false, aRv);
   1146 }
   1147 
   1148 RefPtr<IDBRequest> IDBObjectStore::Get(JSContext* aCx,
   1149                                       JS::Handle<JS::Value> aKey,
   1150                                       ErrorResult& aRv) {
   1151  AssertIsOnOwningThread();
   1152 
   1153  return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv);
   1154 }
   1155 
   1156 RefPtr<IDBRequest> IDBObjectStore::GetKey(JSContext* aCx,
   1157                                          JS::Handle<JS::Value> aKey,
   1158                                          ErrorResult& aRv) {
   1159  AssertIsOnOwningThread();
   1160 
   1161  return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv);
   1162 }
   1163 
   1164 RefPtr<IDBRequest> IDBObjectStore::Clear(JSContext* aCx, ErrorResult& aRv) {
   1165  AssertIsOnOwningThread();
   1166 
   1167  if (mDeletedSpec) {
   1168    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1169    return nullptr;
   1170  }
   1171 
   1172  if (!mTransaction->IsActive()) {
   1173    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1174    return nullptr;
   1175  }
   1176 
   1177  if (!mTransaction->IsWriteAllowed()) {
   1178    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
   1179    return nullptr;
   1180  }
   1181 
   1182  const ObjectStoreClearParams params = {Id()};
   1183 
   1184  auto request = GenerateRequest(aCx, this).unwrap();
   1185 
   1186  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1187      "database(%s).transaction(%s).objectStore(%s).clear()",
   1188      "IDBObjectStore.clear(%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(),
   1189      request->LoggingSerialNumber(),
   1190      IDB_LOG_STRINGIFY(mTransaction->Database()),
   1191      IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this));
   1192 
   1193  mTransaction->InvalidateCursorCaches();
   1194 
   1195  mTransaction->StartRequest(request, params);
   1196 
   1197  return request;
   1198 }
   1199 
   1200 RefPtr<IDBRequest> IDBObjectStore::GetAll(JSContext* aCx,
   1201                                          JS::Handle<JS::Value> aKey,
   1202                                          const Optional<uint32_t>& aLimit,
   1203                                          ErrorResult& aRv) {
   1204  AssertIsOnOwningThread();
   1205 
   1206  return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv);
   1207 }
   1208 
   1209 RefPtr<IDBRequest> IDBObjectStore::GetAllKeys(JSContext* aCx,
   1210                                              JS::Handle<JS::Value> aKey,
   1211                                              const Optional<uint32_t>& aLimit,
   1212                                              ErrorResult& aRv) {
   1213  AssertIsOnOwningThread();
   1214 
   1215  return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv);
   1216 }
   1217 
   1218 RefPtr<IDBRequest> IDBObjectStore::OpenCursor(JSContext* aCx,
   1219                                              JS::Handle<JS::Value> aRange,
   1220                                              IDBCursorDirection aDirection,
   1221                                              ErrorResult& aRv) {
   1222  AssertIsOnOwningThread();
   1223 
   1224  return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection,
   1225                            aRv);
   1226 }
   1227 
   1228 RefPtr<IDBRequest> IDBObjectStore::OpenCursor(JSContext* aCx,
   1229                                              IDBCursorDirection aDirection,
   1230                                              ErrorResult& aRv) {
   1231  AssertIsOnOwningThread();
   1232 
   1233  return OpenCursorInternal(/* aKeysOnly */ false, aCx,
   1234                            JS::UndefinedHandleValue, aDirection, aRv);
   1235 }
   1236 
   1237 RefPtr<IDBRequest> IDBObjectStore::OpenKeyCursor(JSContext* aCx,
   1238                                                 JS::Handle<JS::Value> aRange,
   1239                                                 IDBCursorDirection aDirection,
   1240                                                 ErrorResult& aRv) {
   1241  AssertIsOnOwningThread();
   1242 
   1243  return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, aRv);
   1244 }
   1245 
   1246 RefPtr<IDBIndex> IDBObjectStore::Index(const nsAString& aName,
   1247                                       ErrorResult& aRv) {
   1248  AssertIsOnOwningThread();
   1249 
   1250  if (mTransaction->IsCommittingOrFinished() || mDeletedSpec) {
   1251    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1252    return nullptr;
   1253  }
   1254 
   1255  const nsTArray<IndexMetadata>& indexMetadatas = mSpec->indexes();
   1256 
   1257  const auto endIndexMetadatas = indexMetadatas.cend();
   1258  const auto foundMetadata =
   1259      std::find_if(indexMetadatas.cbegin(), endIndexMetadatas,
   1260                   [&aName](const auto& indexMetadata) {
   1261                     return indexMetadata.name() == aName;
   1262                   });
   1263 
   1264  if (foundMetadata == endIndexMetadatas) {
   1265    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
   1266    return nullptr;
   1267  }
   1268 
   1269  const IndexMetadata& metadata = *foundMetadata;
   1270 
   1271  const auto endIndexes = mIndexes.cend();
   1272  const auto foundIndex =
   1273      std::find_if(mIndexes.cbegin(), endIndexes,
   1274                   [desiredId = metadata.id()](const auto& index) {
   1275                     return index->Id() == desiredId;
   1276                   });
   1277 
   1278  RefPtr<IDBIndex> index;
   1279 
   1280  if (foundIndex == endIndexes) {
   1281    index = IDBIndex::Create(this, metadata);
   1282    MOZ_ASSERT(index);
   1283 
   1284    mIndexes.AppendElement(index);
   1285  } else {
   1286    index = *foundIndex;
   1287  }
   1288 
   1289  return index;
   1290 }
   1291 
   1292 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
   1293 
   1294 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
   1295  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   1296  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
   1297 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   1298 
   1299 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
   1300  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
   1301  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes)
   1302  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedIndexes)
   1303 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1304 
   1305 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
   1306  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1307 
   1308  // Don't unlink mTransaction!
   1309 
   1310  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes)
   1311  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedIndexes)
   1312 
   1313  tmp->mCachedKeyPath.setUndefined();
   1314 
   1315  if (tmp->mRooted) {
   1316    mozilla::DropJSObjects(tmp);
   1317    tmp->mRooted = false;
   1318  }
   1319 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1320 
   1321 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
   1322  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1323  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1324 NS_INTERFACE_MAP_END
   1325 
   1326 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
   1327 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
   1328 
   1329 JSObject* IDBObjectStore::WrapObject(JSContext* aCx,
   1330                                     JS::Handle<JSObject*> aGivenProto) {
   1331  return IDBObjectStore_Binding::Wrap(aCx, this, aGivenProto);
   1332 }
   1333 
   1334 nsIGlobalObject* IDBObjectStore::GetParentObject() const {
   1335  return mTransaction->GetParentObject();
   1336 }
   1337 
   1338 void IDBObjectStore::GetKeyPath(JSContext* aCx,
   1339                                JS::MutableHandle<JS::Value> aResult,
   1340                                ErrorResult& aRv) {
   1341  if (!mCachedKeyPath.isUndefined()) {
   1342    aResult.set(mCachedKeyPath);
   1343    return;
   1344  }
   1345 
   1346  aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   1347  if (NS_WARN_IF(aRv.Failed())) {
   1348    return;
   1349  }
   1350 
   1351  if (mCachedKeyPath.isGCThing()) {
   1352    mozilla::HoldJSObjects(this);
   1353    mRooted = true;
   1354  }
   1355 
   1356  aResult.set(mCachedKeyPath);
   1357 }
   1358 
   1359 RefPtr<DOMStringList> IDBObjectStore::IndexNames() {
   1360  AssertIsOnOwningThread();
   1361 
   1362  return CreateSortedDOMStringList(
   1363      mSpec->indexes(), [](const auto& index) { return index.name(); });
   1364 }
   1365 
   1366 RefPtr<IDBRequest> IDBObjectStore::GetInternal(bool aKeyOnly, JSContext* aCx,
   1367                                               JS::Handle<JS::Value> aKey,
   1368                                               ErrorResult& aRv) {
   1369  AssertIsOnOwningThread();
   1370 
   1371  if (mDeletedSpec) {
   1372    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1373    return nullptr;
   1374  }
   1375 
   1376  if (!mTransaction->IsActive()) {
   1377    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1378    return nullptr;
   1379  }
   1380 
   1381  RefPtr<IDBKeyRange> keyRange;
   1382  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
   1383  if (aRv.Failed()) {
   1384    return nullptr;
   1385  }
   1386 
   1387  if (!keyRange) {
   1388    // Must specify a key or keyRange for get().
   1389    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
   1390    return nullptr;
   1391  }
   1392 
   1393  const int64_t id = Id();
   1394 
   1395  SerializedKeyRange serializedKeyRange;
   1396  keyRange->ToSerialized(serializedKeyRange);
   1397 
   1398  const auto& params =
   1399      aKeyOnly ? RequestParams{ObjectStoreGetKeyParams(id, serializedKeyRange)}
   1400               : RequestParams{ObjectStoreGetParams(id, serializedKeyRange)};
   1401 
   1402  auto request = GenerateRequest(aCx, this).unwrap();
   1403 
   1404  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1405      "database(%s).transaction(%s).objectStore(%s).get(%s)",
   1406      "IDBObjectStore.get(%.0s%.0s%.0s%.0s)",
   1407      mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1408      IDB_LOG_STRINGIFY(mTransaction->Database()),
   1409      IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1410      IDB_LOG_STRINGIFY(keyRange));
   1411 
   1412  // TODO: This is necessary to preserve request ordering only. Proper
   1413  // sequencing of requests should be done in a more sophisticated manner that
   1414  // doesn't require invalidating cursor caches (Bug 1580499).
   1415  mTransaction->InvalidateCursorCaches();
   1416 
   1417  mTransaction->StartRequest(request, params);
   1418 
   1419  return request;
   1420 }
   1421 
   1422 RefPtr<IDBRequest> IDBObjectStore::DeleteInternal(JSContext* aCx,
   1423                                                  JS::Handle<JS::Value> aKey,
   1424                                                  bool aFromCursor,
   1425                                                  ErrorResult& aRv) {
   1426  AssertIsOnOwningThread();
   1427 
   1428  if (mDeletedSpec) {
   1429    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1430    return nullptr;
   1431  }
   1432 
   1433  if (!mTransaction->IsActive()) {
   1434    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1435    return nullptr;
   1436  }
   1437 
   1438  if (!mTransaction->IsWriteAllowed()) {
   1439    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
   1440    return nullptr;
   1441  }
   1442 
   1443  RefPtr<IDBKeyRange> keyRange;
   1444  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
   1445  if (NS_WARN_IF((aRv.Failed()))) {
   1446    return nullptr;
   1447  }
   1448 
   1449  if (!keyRange) {
   1450    // Must specify a key or keyRange for delete().
   1451    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
   1452    return nullptr;
   1453  }
   1454 
   1455  ObjectStoreDeleteParams params;
   1456  params.objectStoreId() = Id();
   1457  keyRange->ToSerialized(params.keyRange());
   1458 
   1459  auto request = GenerateRequest(aCx, this).unwrap();
   1460 
   1461  if (!aFromCursor) {
   1462    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1463        "database(%s).transaction(%s).objectStore(%s).delete(%s)",
   1464        "IDBObjectStore.delete(%.0s%.0s%.0s%.0s)",
   1465        mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1466        IDB_LOG_STRINGIFY(mTransaction->Database()),
   1467        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1468        IDB_LOG_STRINGIFY(keyRange));
   1469  }
   1470 
   1471  mTransaction->StartRequest(request, params);
   1472 
   1473  mTransaction->InvalidateCursorCaches();
   1474 
   1475  return request;
   1476 }
   1477 
   1478 RefPtr<IDBIndex> IDBObjectStore::CreateIndex(
   1479    const nsAString& aName, const StringOrStringSequence& aKeyPath,
   1480    const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv) {
   1481  AssertIsOnOwningThread();
   1482 
   1483  if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
   1484      mDeletedSpec) {
   1485    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1486    return nullptr;
   1487  }
   1488 
   1489  const auto transaction = IDBTransaction::MaybeCurrent();
   1490  if (!transaction || transaction != mTransaction || !transaction->IsActive()) {
   1491    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1492    return nullptr;
   1493  }
   1494 
   1495  const auto& indexes = mSpec->indexes();
   1496  const auto end = indexes.cend();
   1497  const auto foundIt = std::find_if(
   1498      indexes.cbegin(), end,
   1499      [&aName](const auto& index) { return aName == index.name(); });
   1500  if (foundIt != end) {
   1501    aRv.ThrowConstraintError(nsPrintfCString(
   1502        "Index named '%s' already exists at index '%zu'",
   1503        NS_ConvertUTF16toUTF8(aName).get(), foundIt.GetIndex()));
   1504    return nullptr;
   1505  }
   1506 
   1507  const auto checkValid = [](const auto& keyPath) -> Result<KeyPath, nsresult> {
   1508    if (!keyPath.IsValid()) {
   1509      return Err(NS_ERROR_DOM_SYNTAX_ERR);
   1510    }
   1511 
   1512    return keyPath;
   1513  };
   1514 
   1515  QM_INFOONLY_TRY_UNWRAP(
   1516      const auto maybeKeyPath,
   1517      ([&aKeyPath, checkValid]() -> Result<KeyPath, nsresult> {
   1518        if (aKeyPath.IsString()) {
   1519          QM_TRY_RETURN(
   1520              KeyPath::Parse(aKeyPath.GetAsString()).andThen(checkValid));
   1521        }
   1522 
   1523        MOZ_ASSERT(aKeyPath.IsStringSequence());
   1524        if (aKeyPath.GetAsStringSequence().IsEmpty()) {
   1525          return Err(NS_ERROR_DOM_SYNTAX_ERR);
   1526        }
   1527 
   1528        QM_TRY_RETURN(
   1529            KeyPath::Parse(aKeyPath.GetAsStringSequence()).andThen(checkValid));
   1530      })());
   1531  if (!maybeKeyPath) {
   1532    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1533    return nullptr;
   1534  }
   1535 
   1536  const auto& keyPath = maybeKeyPath.ref();
   1537 
   1538  if (aOptionalParameters.mMultiEntry && keyPath.IsArray()) {
   1539    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   1540    return nullptr;
   1541  }
   1542 
   1543 #ifdef DEBUG
   1544  {
   1545    const auto duplicateIndexName = std::any_of(
   1546        mIndexes.cbegin(), mIndexes.cend(),
   1547        [&aName](const auto& index) { return index->Name() == aName; });
   1548    MOZ_ASSERT(!duplicateIndexName);
   1549  }
   1550 #endif
   1551 
   1552  const IndexMetadata* const oldMetadataElements =
   1553      indexes.IsEmpty() ? nullptr : indexes.Elements();
   1554 
   1555  // With this setup we only validate the passed in locale name by the time we
   1556  // get to encoding Keys. Maybe we should do it here right away and error out.
   1557 
   1558  // Valid locale names are always ASCII as per BCP-47.
   1559  nsCString locale = NS_LossyConvertUTF16toASCII(aOptionalParameters.mLocale);
   1560  bool autoLocale = locale.EqualsASCII("auto");
   1561  if (autoLocale) {
   1562    locale = IndexedDatabaseManager::GetLocale();
   1563  }
   1564 
   1565  if (!locale.IsEmpty()) {
   1566    // Set use counter and log deprecation warning for locale in parent doc.
   1567    nsIGlobalObject* global = GetParentObject();
   1568    AutoJSAPI jsapi;
   1569    // This isn't critical so don't error out if init fails.
   1570    if (jsapi.Init(global)) {
   1571      DeprecationWarning(
   1572          jsapi.cx(), global->GetGlobalJSObject(),
   1573          DeprecatedOperations::eIDBObjectStoreCreateIndexLocale);
   1574    }
   1575  }
   1576 
   1577  IndexMetadata* const metadata = mSpec->indexes().EmplaceBack(
   1578      transaction->NextIndexId(), nsString(aName), keyPath, locale,
   1579      aOptionalParameters.mUnique, aOptionalParameters.mMultiEntry, autoLocale);
   1580 
   1581  if (oldMetadataElements && oldMetadataElements != indexes.Elements()) {
   1582    MOZ_ASSERT(indexes.Length() > 1);
   1583 
   1584    // Array got moved, update the spec pointers for all live indexes.
   1585    RefreshSpec(/* aMayDelete */ false);
   1586  }
   1587 
   1588  transaction->CreateIndex(this, *metadata);
   1589 
   1590  auto index = IDBIndex::Create(this, *metadata);
   1591 
   1592  mIndexes.AppendElement(index);
   1593 
   1594  // Don't do this in the macro because we always need to increment the serial
   1595  // number to keep in sync with the parent.
   1596  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
   1597 
   1598  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1599      "database(%s).transaction(%s).objectStore(%s).createIndex(%s)",
   1600      "IDBObjectStore.createIndex(%.0s%.0s%.0s%.0s)",
   1601      mTransaction->LoggingSerialNumber(), requestSerialNumber,
   1602      IDB_LOG_STRINGIFY(mTransaction->Database()),
   1603      IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1604      IDB_LOG_STRINGIFY(index));
   1605 
   1606  return index;
   1607 }
   1608 
   1609 void IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) {
   1610  AssertIsOnOwningThread();
   1611 
   1612  if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
   1613      mDeletedSpec) {
   1614    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1615    return;
   1616  }
   1617 
   1618  const auto transaction = IDBTransaction::MaybeCurrent();
   1619  if (!transaction || transaction != mTransaction || !transaction->IsActive()) {
   1620    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1621    return;
   1622  }
   1623 
   1624  const auto& metadataArray = mSpec->indexes();
   1625 
   1626  const auto endMetadata = metadataArray.cend();
   1627  const auto foundMetadataIt = std::find_if(
   1628      metadataArray.cbegin(), endMetadata,
   1629      [&aName](const auto& metadata) { return aName == metadata.name(); });
   1630 
   1631  if (foundMetadataIt == endMetadata) {
   1632    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
   1633    return;
   1634  }
   1635 
   1636  const auto foundId = foundMetadataIt->id();
   1637  MOZ_ASSERT(foundId);
   1638 
   1639  // Must remove index from mIndexes before altering the metadata array!
   1640  {
   1641    const auto end = mIndexes.end();
   1642    const auto foundIt = std::find_if(
   1643        mIndexes.begin(), end,
   1644        [foundId](const auto& index) { return index->Id() == foundId; });
   1645    // TODO: Or should we assert foundIt != end?
   1646    if (foundIt != end) {
   1647      auto& index = *foundIt;
   1648 
   1649      index->NoteDeletion();
   1650 
   1651      mDeletedIndexes.EmplaceBack(std::move(index));
   1652      mIndexes.RemoveElementAt(foundIt.GetIndex());
   1653    }
   1654  }
   1655 
   1656  mSpec->indexes().RemoveElementAt(foundMetadataIt.GetIndex());
   1657 
   1658  RefreshSpec(/* aMayDelete */ false);
   1659 
   1660  // Don't do this in the macro because we always need to increment the serial
   1661  // number to keep in sync with the parent.
   1662  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
   1663 
   1664  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1665      "database(%s).transaction(%s).objectStore(%s)."
   1666      "deleteIndex(\"%s\")",
   1667      "IDBObjectStore.deleteIndex(%.0s%.0s%.0s%.0s)",
   1668      mTransaction->LoggingSerialNumber(), requestSerialNumber,
   1669      IDB_LOG_STRINGIFY(mTransaction->Database()),
   1670      IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1671      NS_ConvertUTF16toUTF8(aName).get());
   1672 
   1673  transaction->DeleteIndex(this, foundId);
   1674 }
   1675 
   1676 RefPtr<IDBRequest> IDBObjectStore::Count(JSContext* aCx,
   1677                                         JS::Handle<JS::Value> aKey,
   1678                                         ErrorResult& aRv) {
   1679  AssertIsOnOwningThread();
   1680 
   1681  if (mDeletedSpec) {
   1682    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1683    return nullptr;
   1684  }
   1685 
   1686  if (!mTransaction->IsActive()) {
   1687    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1688    return nullptr;
   1689  }
   1690 
   1691  RefPtr<IDBKeyRange> keyRange;
   1692  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
   1693  if (aRv.Failed()) {
   1694    return nullptr;
   1695  }
   1696 
   1697  ObjectStoreCountParams params;
   1698  params.objectStoreId() = Id();
   1699 
   1700  if (keyRange) {
   1701    SerializedKeyRange serializedKeyRange;
   1702    keyRange->ToSerialized(serializedKeyRange);
   1703    params.optionalKeyRange().emplace(serializedKeyRange);
   1704  }
   1705 
   1706  auto request = GenerateRequest(aCx, this).unwrap();
   1707 
   1708  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1709      "database(%s).transaction(%s).objectStore(%s).count(%s)",
   1710      "IDBObjectStore.count(%.0s%.0s%.0s%.0s)",
   1711      mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1712      IDB_LOG_STRINGIFY(mTransaction->Database()),
   1713      IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1714      IDB_LOG_STRINGIFY(keyRange));
   1715 
   1716  // TODO: This is necessary to preserve request ordering only. Proper
   1717  // sequencing of requests should be done in a more sophisticated manner that
   1718  // doesn't require invalidating cursor caches (Bug 1580499).
   1719  mTransaction->InvalidateCursorCaches();
   1720 
   1721  mTransaction->StartRequest(request, params);
   1722 
   1723  return request;
   1724 }
   1725 
   1726 RefPtr<IDBRequest> IDBObjectStore::OpenCursorInternal(
   1727    bool aKeysOnly, JSContext* aCx, JS::Handle<JS::Value> aRange,
   1728    IDBCursorDirection aDirection, ErrorResult& aRv) {
   1729  AssertIsOnOwningThread();
   1730  MOZ_ASSERT(aCx);
   1731 
   1732  if (mDeletedSpec) {
   1733    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   1734    return nullptr;
   1735  }
   1736 
   1737  if (!mTransaction->IsActive()) {
   1738    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1739    return nullptr;
   1740  }
   1741 
   1742  RefPtr<IDBKeyRange> keyRange;
   1743  IDBKeyRange::FromJSVal(aCx, aRange, &keyRange, aRv);
   1744  if (NS_WARN_IF(aRv.Failed())) {
   1745    return nullptr;
   1746  }
   1747 
   1748  const int64_t objectStoreId = Id();
   1749 
   1750  Maybe<SerializedKeyRange> optionalKeyRange;
   1751 
   1752  if (keyRange) {
   1753    SerializedKeyRange serializedKeyRange;
   1754    keyRange->ToSerialized(serializedKeyRange);
   1755 
   1756    optionalKeyRange.emplace(std::move(serializedKeyRange));
   1757  }
   1758 
   1759  const CommonOpenCursorParams commonParams = {
   1760      objectStoreId, std::move(optionalKeyRange), aDirection};
   1761 
   1762  // TODO: It would be great if the IPDL generator created a constructor
   1763  // accepting a CommonOpenCursorParams by value or rvalue reference.
   1764  const auto params =
   1765      aKeysOnly ? OpenCursorParams{ObjectStoreOpenKeyCursorParams{commonParams}}
   1766                : OpenCursorParams{ObjectStoreOpenCursorParams{commonParams}};
   1767 
   1768  auto request = GenerateRequest(aCx, this).unwrap();
   1769 
   1770  if (aKeysOnly) {
   1771    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1772        "database(%s).transaction(%s).objectStore(%s)."
   1773        "openKeyCursor(%s, %s)",
   1774        "IDBObjectStore.openKeyCursor(%.0s%.0s%.0s%.0s%.0s)",
   1775        mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1776        IDB_LOG_STRINGIFY(mTransaction->Database()),
   1777        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1778        IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aDirection));
   1779  } else {
   1780    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1781        "database(%s).transaction(%s).objectStore(%s)."
   1782        "openCursor(%s, %s)",
   1783        "IDBObjectStore.openCursor(%.0s%.0s%.0s%.0s%.0s)",
   1784        mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
   1785        IDB_LOG_STRINGIFY(mTransaction->Database()),
   1786        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(this),
   1787        IDB_LOG_STRINGIFY(keyRange), IDB_LOG_STRINGIFY(aDirection));
   1788  }
   1789 
   1790  const auto actor =
   1791      aKeysOnly
   1792          ? static_cast<SafeRefPtr<BackgroundCursorChildBase>>(
   1793                MakeSafeRefPtr<
   1794                    BackgroundCursorChild<IDBCursorType::ObjectStoreKey>>(
   1795                    request, this, aDirection))
   1796          : MakeSafeRefPtr<BackgroundCursorChild<IDBCursorType::ObjectStore>>(
   1797                request, this, aDirection);
   1798 
   1799  // TODO: This is necessary to preserve request ordering only. Proper
   1800  // sequencing of requests should be done in a more sophisticated manner that
   1801  // doesn't require invalidating cursor caches (Bug 1580499).
   1802  mTransaction->InvalidateCursorCaches();
   1803 
   1804  mTransaction->OpenCursor(*actor, params);
   1805 
   1806  return request;
   1807 }
   1808 
   1809 void IDBObjectStore::RefreshSpec(bool aMayDelete) {
   1810  AssertIsOnOwningThread();
   1811  MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec.get());
   1812 
   1813  auto* const foundObjectStoreSpec =
   1814      mTransaction->Database()->LookupModifiableObjectStoreSpec(
   1815          [id = Id()](const auto& objSpec) {
   1816            return objSpec.metadata().id() == id;
   1817          });
   1818  if (foundObjectStoreSpec) {
   1819    mSpec = foundObjectStoreSpec;
   1820 
   1821    for (auto& index : mIndexes) {
   1822      index->RefreshMetadata(aMayDelete);
   1823    }
   1824 
   1825    for (auto& index : mDeletedIndexes) {
   1826      index->RefreshMetadata(false);
   1827    }
   1828  }
   1829 
   1830  MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, foundObjectStoreSpec);
   1831 
   1832  if (foundObjectStoreSpec) {
   1833    MOZ_ASSERT(mSpec != mDeletedSpec.get());
   1834    mDeletedSpec = nullptr;
   1835  } else {
   1836    NoteDeletion();
   1837  }
   1838 }
   1839 
   1840 const ObjectStoreSpec& IDBObjectStore::Spec() const {
   1841  AssertIsOnOwningThread();
   1842  MOZ_ASSERT(mSpec);
   1843 
   1844  return *mSpec;
   1845 }
   1846 
   1847 void IDBObjectStore::NoteDeletion() {
   1848  AssertIsOnOwningThread();
   1849  MOZ_ASSERT(mSpec);
   1850  MOZ_ASSERT(Id() == mSpec->metadata().id());
   1851 
   1852  if (mDeletedSpec) {
   1853    MOZ_ASSERT(mDeletedSpec.get() == mSpec);
   1854    return;
   1855  }
   1856 
   1857  // Copy the spec here.
   1858  mDeletedSpec = MakeUnique<ObjectStoreSpec>(*mSpec);
   1859  mDeletedSpec->indexes().Clear();
   1860 
   1861  mSpec = mDeletedSpec.get();
   1862 
   1863  for (const auto& index : mIndexes) {
   1864    index->NoteDeletion();
   1865  }
   1866 }
   1867 
   1868 const nsString& IDBObjectStore::Name() const {
   1869  AssertIsOnOwningThread();
   1870  MOZ_ASSERT(mSpec);
   1871 
   1872  return mSpec->metadata().name();
   1873 }
   1874 
   1875 void IDBObjectStore::SetName(const nsAString& aName, ErrorResult& aRv) {
   1876  AssertIsOnOwningThread();
   1877 
   1878  if (mTransaction->GetMode() != IDBTransaction::Mode::VersionChange ||
   1879      mDeletedSpec) {
   1880    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1881    return;
   1882  }
   1883 
   1884  const auto transaction = IDBTransaction::MaybeCurrent();
   1885  if (!transaction || transaction != mTransaction || !transaction->IsActive()) {
   1886    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
   1887    return;
   1888  }
   1889 
   1890  if (aName == mSpec->metadata().name()) {
   1891    return;
   1892  }
   1893 
   1894  // Cache logging string of this object store before renaming.
   1895  const LoggingString loggingOldObjectStore(this);
   1896 
   1897  const nsresult rv =
   1898      transaction->Database()->RenameObjectStore(mSpec->metadata().id(), aName);
   1899 
   1900  if (NS_FAILED(rv)) {
   1901    aRv.Throw(rv);
   1902    return;
   1903  }
   1904 
   1905  // Don't do this in the macro because we always need to increment the serial
   1906  // number to keep in sync with the parent.
   1907  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
   1908 
   1909  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
   1910      "database(%s).transaction(%s).objectStore(%s).rename(%s)",
   1911      "IDBObjectStore.rename(%.0s%.0s%.0s%.0s)",
   1912      mTransaction->LoggingSerialNumber(), requestSerialNumber,
   1913      IDB_LOG_STRINGIFY(mTransaction->Database()),
   1914      IDB_LOG_STRINGIFY(*mTransaction), loggingOldObjectStore.get(),
   1915      IDB_LOG_STRINGIFY(this));
   1916 
   1917  transaction->RenameObjectStore(mSpec->metadata().id(), aName);
   1918 }
   1919 
   1920 bool IDBObjectStore::AutoIncrement() const {
   1921  AssertIsOnOwningThread();
   1922  MOZ_ASSERT(mSpec);
   1923 
   1924  return mSpec->metadata().autoIncrement();
   1925 }
   1926 
   1927 const indexedDB::KeyPath& IDBObjectStore::GetKeyPath() const {
   1928  AssertIsOnOwningThread();
   1929  MOZ_ASSERT(mSpec);
   1930 
   1931  return mSpec->metadata().keyPath();
   1932 }
   1933 
   1934 bool IDBObjectStore::HasValidKeyPath() const {
   1935  AssertIsOnOwningThread();
   1936  MOZ_ASSERT(mSpec);
   1937 
   1938  return GetKeyPath().IsValid();
   1939 }
   1940 
   1941 bool IDBObjectStore::ValueWrapper::Clone(JSContext* aCx) {
   1942  if (mCloned) {
   1943    return true;
   1944  }
   1945 
   1946  static const JSStructuredCloneCallbacks callbacks = {
   1947      CopyingStructuredCloneReadCallback /* read */,
   1948      CopyingStructuredCloneWriteCallback /* write */,
   1949      StructuredCloneErrorCallback /* reportError */,
   1950      nullptr /* readTransfer */,
   1951      nullptr /* writeTransfer */,
   1952      nullptr /* freeTransfer */,
   1953      nullptr /* canTransfer */,
   1954      nullptr /* sabCloned */
   1955  };
   1956 
   1957  StructuredCloneInfo cloneInfo;
   1958 
   1959  JS::Rooted<JS::Value> clonedValue(aCx);
   1960  if (!JS_StructuredClone(aCx, mValue, &clonedValue, &callbacks, &cloneInfo)) {
   1961    return false;
   1962  }
   1963 
   1964  mValue = clonedValue;
   1965 
   1966  mCloned = true;
   1967 
   1968  return true;
   1969 }
   1970 
   1971 }  // namespace mozilla::dom