tor-browser

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

IDBCursor.cpp (27366B)


      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 "IDBCursor.h"
      8 
      9 #include "IDBDatabase.h"
     10 #include "IDBIndex.h"
     11 #include "IDBObjectStore.h"
     12 #include "IDBRequest.h"
     13 #include "IDBTransaction.h"
     14 #include "IndexedDatabaseInlines.h"
     15 #include "ProfilerHelpers.h"
     16 #include "ReportInternalError.h"
     17 #include "mozilla/ErrorResult.h"
     18 #include "mozilla/HoldDropJSObjects.h"
     19 #include "mozilla/dom/UnionTypes.h"
     20 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
     21 #include "nsString.h"
     22 
     23 // Include this last to avoid path problems on Windows.
     24 #include "ActorsChild.h"
     25 
     26 namespace mozilla::dom {
     27 
     28 using namespace indexedDB;
     29 
     30 IDBCursor::IDBCursor(BackgroundCursorChildBase* const aBackgroundActor)
     31    : mBackgroundActor(WrapNotNull(aBackgroundActor)),
     32      mRequest(aBackgroundActor->GetRequest()),
     33      mTransaction(&mRequest->MutableTransactionRef()),
     34      mCachedKey(JS::UndefinedValue()),
     35      mCachedPrimaryKey(JS::UndefinedValue()),
     36      mCachedValue(JS::UndefinedValue()),
     37      mDirection(aBackgroundActor->GetDirection()),
     38      mHaveCachedKey(false),
     39      mHaveCachedPrimaryKey(false),
     40      mHaveCachedValue(false),
     41      mRooted(false),
     42      mContinueCalled(false),
     43      mHaveValue(true) {
     44  MOZ_ASSERT(aBackgroundActor);
     45  aBackgroundActor->AssertIsOnOwningThread();
     46  MOZ_ASSERT(mRequest);
     47 
     48  mTransaction->RegisterCursor(*this);
     49 }
     50 
     51 template <IDBCursor::Type CursorType>
     52 IDBTypedCursor<CursorType>::~IDBTypedCursor() {
     53  AssertIsOnOwningThread();
     54 
     55  mTransaction->UnregisterCursor(*this);
     56 
     57  DropJSObjects();
     58 
     59  if (mBackgroundActor) {
     60    (*mBackgroundActor)->SendDeleteMeInternal();
     61    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
     62  }
     63 
     64  // Let's explicitly not leave any dangling CheckedUnsafePtr.
     65  mTransaction = nullptr;
     66 }
     67 
     68 // static
     69 RefPtr<IDBObjectStoreCursor> IDBCursor::Create(
     70    BackgroundCursorChild<Type::ObjectStore>* const aBackgroundActor, Key aKey,
     71    StructuredCloneReadInfoChild&& aCloneInfo) {
     72  MOZ_ASSERT(aBackgroundActor);
     73  aBackgroundActor->AssertIsOnOwningThread();
     74  MOZ_ASSERT(!aKey.IsUnset());
     75 
     76  return MakeRefPtr<IDBObjectStoreCursor>(aBackgroundActor, std::move(aKey),
     77                                          std::move(aCloneInfo));
     78 }
     79 
     80 // static
     81 RefPtr<IDBObjectStoreKeyCursor> IDBCursor::Create(
     82    BackgroundCursorChild<Type::ObjectStoreKey>* const aBackgroundActor,
     83    Key aKey) {
     84  MOZ_ASSERT(aBackgroundActor);
     85  aBackgroundActor->AssertIsOnOwningThread();
     86  MOZ_ASSERT(!aKey.IsUnset());
     87 
     88  return MakeRefPtr<IDBObjectStoreKeyCursor>(aBackgroundActor, std::move(aKey));
     89 }
     90 
     91 // static
     92 RefPtr<IDBIndexCursor> IDBCursor::Create(
     93    BackgroundCursorChild<Type::Index>* const aBackgroundActor, Key aKey,
     94    Key aSortKey, Key aPrimaryKey, StructuredCloneReadInfoChild&& aCloneInfo) {
     95  MOZ_ASSERT(aBackgroundActor);
     96  aBackgroundActor->AssertIsOnOwningThread();
     97  MOZ_ASSERT(!aKey.IsUnset());
     98  MOZ_ASSERT(!aPrimaryKey.IsUnset());
     99 
    100  return MakeRefPtr<IDBIndexCursor>(aBackgroundActor, std::move(aKey),
    101                                    std::move(aSortKey), std::move(aPrimaryKey),
    102                                    std::move(aCloneInfo));
    103 }
    104 
    105 // static
    106 RefPtr<IDBIndexKeyCursor> IDBCursor::Create(
    107    BackgroundCursorChild<Type::IndexKey>* const aBackgroundActor, Key aKey,
    108    Key aSortKey, Key aPrimaryKey) {
    109  MOZ_ASSERT(aBackgroundActor);
    110  aBackgroundActor->AssertIsOnOwningThread();
    111  MOZ_ASSERT(!aKey.IsUnset());
    112  MOZ_ASSERT(!aPrimaryKey.IsUnset());
    113 
    114  return MakeRefPtr<IDBIndexKeyCursor>(aBackgroundActor, std::move(aKey),
    115                                       std::move(aSortKey),
    116                                       std::move(aPrimaryKey));
    117 }
    118 
    119 #ifdef DEBUG
    120 
    121 void IDBCursor::AssertIsOnOwningThread() const {
    122  MOZ_ASSERT(mTransaction);
    123  mTransaction->AssertIsOnOwningThread();
    124 }
    125 
    126 #endif  // DEBUG
    127 
    128 template <IDBCursor::Type CursorType>
    129 void IDBTypedCursor<CursorType>::DropJSObjects() {
    130  AssertIsOnOwningThread();
    131 
    132  Reset();
    133 
    134  if (!mRooted) {
    135    return;
    136  }
    137 
    138  mRooted = false;
    139 
    140  mozilla::DropJSObjects(this);
    141 }
    142 
    143 template <IDBCursor::Type CursorType>
    144 bool IDBTypedCursor<CursorType>::IsSourceDeleted() const {
    145  AssertIsOnOwningThread();
    146  MOZ_ASSERT(mTransaction);
    147  MOZ_ASSERT(mTransaction->IsActive());
    148 
    149  const auto* const sourceObjectStore = [this]() -> const IDBObjectStore* {
    150    if constexpr (IsObjectStoreCursor) {
    151      return mSource;
    152    } else {
    153      if (GetSourceRef().IsDeleted()) {
    154        return nullptr;
    155      }
    156 
    157      const auto* const res = GetSourceRef().ObjectStore();
    158      MOZ_ASSERT(res);
    159      return res;
    160    }
    161  }();
    162 
    163  return !sourceObjectStore || sourceObjectStore->IsDeleted();
    164 }
    165 
    166 void IDBCursor::ResetBase() {
    167  AssertIsOnOwningThread();
    168 
    169  mCachedKey.setUndefined();
    170  mCachedPrimaryKey.setUndefined();
    171  mCachedValue.setUndefined();
    172 
    173  mHaveCachedKey = false;
    174  mHaveCachedPrimaryKey = false;
    175  mHaveCachedValue = false;
    176  mHaveValue = false;
    177  mContinueCalled = false;
    178 }
    179 
    180 template <IDBCursor::Type CursorType>
    181 void IDBTypedCursor<CursorType>::Reset() {
    182  AssertIsOnOwningThread();
    183 
    184  if constexpr (!IsKeyOnlyCursor) {
    185    IDBObjectStore::ClearCloneReadInfo(mData.mCloneInfo);
    186  }
    187 
    188  ResetBase();
    189 }
    190 
    191 nsIGlobalObject* IDBCursor::GetParentObject() const {
    192  AssertIsOnOwningThread();
    193  MOZ_ASSERT(mTransaction);
    194 
    195  return mTransaction->GetParentObject();
    196 }
    197 
    198 IDBCursorDirection IDBCursor::GetDirection() const {
    199  AssertIsOnOwningThread();
    200 
    201  switch (mDirection) {
    202    case Direction::Next:
    203      return IDBCursorDirection::Next;
    204 
    205    case Direction::Nextunique:
    206      return IDBCursorDirection::Nextunique;
    207 
    208    case Direction::Prev:
    209      return IDBCursorDirection::Prev;
    210 
    211    case Direction::Prevunique:
    212      return IDBCursorDirection::Prevunique;
    213 
    214    default:
    215      MOZ_CRASH("Bad direction!");
    216  }
    217 }
    218 
    219 RefPtr<IDBRequest> IDBCursor::Request() const {
    220  AssertIsOnOwningThread();
    221  return mRequest;
    222 }
    223 
    224 template <IDBCursor::Type CursorType>
    225 void IDBTypedCursor<CursorType>::GetSource(
    226    OwningIDBObjectStoreOrIDBIndex& aSource) const {
    227  AssertIsOnOwningThread();
    228 
    229  if constexpr (IsObjectStoreCursor) {
    230    aSource.SetAsIDBObjectStore() = mSource;
    231  } else {
    232    aSource.SetAsIDBIndex() = mSource;
    233  }
    234 }
    235 
    236 template <IDBCursor::Type CursorType>
    237 void IDBTypedCursor<CursorType>::GetKey(JSContext* const aCx,
    238                                        JS::MutableHandle<JS::Value> aResult,
    239                                        ErrorResult& aRv) {
    240  AssertIsOnOwningThread();
    241  MOZ_ASSERT(!mData.mKey.IsUnset() || !mHaveValue);
    242 
    243  if (!mHaveValue) {
    244    aResult.setUndefined();
    245    return;
    246  }
    247 
    248  if (!mHaveCachedKey) {
    249    if (!mRooted) {
    250      mozilla::HoldJSObjects(this);
    251      mRooted = true;
    252    }
    253 
    254    aRv = mData.mKey.ToJSVal(aCx, mCachedKey);
    255    if (NS_WARN_IF(aRv.Failed())) {
    256      return;
    257    }
    258 
    259    mHaveCachedKey = true;
    260  }
    261 
    262  aResult.set(mCachedKey);
    263 }
    264 
    265 template <IDBCursor::Type CursorType>
    266 void IDBTypedCursor<CursorType>::GetPrimaryKey(
    267    JSContext* const aCx, JS::MutableHandle<JS::Value> aResult,
    268    ErrorResult& aRv) {
    269  AssertIsOnOwningThread();
    270 
    271  if (!mHaveValue) {
    272    aResult.setUndefined();
    273    return;
    274  }
    275 
    276  if (!mHaveCachedPrimaryKey) {
    277    if (!mRooted) {
    278      mozilla::HoldJSObjects(this);
    279      mRooted = true;
    280    }
    281 
    282    const Key& key = mData.GetObjectStoreKey();
    283 
    284    MOZ_ASSERT(!key.IsUnset());
    285 
    286    aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
    287    if (NS_WARN_IF(aRv.Failed())) {
    288      return;
    289    }
    290 
    291    mHaveCachedPrimaryKey = true;
    292  }
    293 
    294  aResult.set(mCachedPrimaryKey);
    295 }
    296 
    297 template <IDBCursor::Type CursorType>
    298 void IDBTypedCursor<CursorType>::GetValue(JSContext* const aCx,
    299                                          JS::MutableHandle<JS::Value> aResult,
    300                                          ErrorResult& aRv) {
    301  AssertIsOnOwningThread();
    302 
    303  if constexpr (!IsKeyOnlyCursor) {
    304    if (!mHaveValue) {
    305      aResult.setUndefined();
    306      return;
    307    }
    308 
    309    if (!mHaveCachedValue) {
    310      if (!mRooted) {
    311        mozilla::HoldJSObjects(this);
    312        mRooted = true;
    313      }
    314 
    315      JS::Rooted<JS::Value> val(aCx);
    316      if (NS_WARN_IF(!IDBObjectStore::DeserializeValue(
    317              aCx, std::move(mData.mCloneInfo), &val))) {
    318        aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
    319        return;
    320      }
    321 
    322      // XXX This seems redundant, sine mData.mCloneInfo is moved above.
    323      IDBObjectStore::ClearCloneReadInfo(mData.mCloneInfo);
    324 
    325      mCachedValue = val;
    326      mHaveCachedValue = true;
    327    }
    328 
    329    aResult.set(mCachedValue);
    330  } else {
    331    MOZ_CRASH("This shouldn't be callable on a key-only cursor.");
    332  }
    333 }
    334 
    335 template <IDBCursor::Type CursorType>
    336 void IDBTypedCursor<CursorType>::Continue(JSContext* const aCx,
    337                                          JS::Handle<JS::Value> aKey,
    338                                          ErrorResult& aRv) {
    339  AssertIsOnOwningThread();
    340 
    341  if (!mTransaction->IsActive()) {
    342    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    343    return;
    344  }
    345 
    346  if (IsSourceDeleted() || !mHaveValue || mContinueCalled) {
    347    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    348    return;
    349  }
    350 
    351  Key key;
    352  auto result = key.SetFromJSVal(aCx, aKey);
    353  if (result.isErr()) {
    354    aRv = result.unwrapErr().ExtractErrorResult(
    355        InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
    356    return;
    357  }
    358 
    359  if constexpr (!IsObjectStoreCursor) {
    360    if (IsLocaleAware() && !key.IsUnset()) {
    361      auto result = key.ToLocaleAwareKey(GetSourceRef().Locale());
    362      if (result.isErr()) {
    363        aRv.Throw(result.inspectErr());
    364        return;
    365      }
    366      key = result.unwrap();
    367    }
    368  }
    369 
    370  const Key& sortKey = mData.GetSortKey(IsLocaleAware());
    371 
    372  if (!key.IsUnset()) {
    373    switch (mDirection) {
    374      case Direction::Next:
    375      case Direction::Nextunique:
    376        if (key <= sortKey) {
    377          aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    378          return;
    379        }
    380        break;
    381 
    382      case Direction::Prev:
    383      case Direction::Prevunique:
    384        if (key >= sortKey) {
    385          aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    386          return;
    387        }
    388        break;
    389 
    390      default:
    391        MOZ_CRASH("Unknown direction type!");
    392    }
    393  }
    394 
    395  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
    396  mRequest->SetLoggingSerialNumber(requestSerialNumber);
    397 
    398  if constexpr (IsObjectStoreCursor) {
    399    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    400        "database(%s).transaction(%s).objectStore(%s)."
    401        "cursor(%s).continue(%s)",
    402        "IDBCursor.continue(%.0s%.0s%.0s%.0s%.0s)",
    403        mTransaction->LoggingSerialNumber(), requestSerialNumber,
    404        IDB_LOG_STRINGIFY(mTransaction->Database()),
    405        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(mSource),
    406        IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key));
    407  } else {
    408    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    409        "database(%s).transaction(%s).objectStore(%s)."
    410        "index(%s).cursor(%s).continue(%s)",
    411        "IDBCursor.continue(%.0s%.0s%.0s%.0s%.0s%.0s)",
    412        mTransaction->LoggingSerialNumber(), requestSerialNumber,
    413        IDB_LOG_STRINGIFY(mTransaction->Database()),
    414        IDB_LOG_STRINGIFY(*mTransaction),
    415        IDB_LOG_STRINGIFY(GetSourceRef().ObjectStore()),
    416        IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
    417        IDB_LOG_STRINGIFY(key));
    418  }
    419 
    420  GetTypedBackgroundActorRef().SendContinueInternal(
    421      mTransaction->NextRequestId(), ContinueParams(key), mData);
    422 
    423  mContinueCalled = true;
    424 }
    425 
    426 template <IDBCursor::Type CursorType>
    427 void IDBTypedCursor<CursorType>::ContinuePrimaryKey(
    428    JSContext* const aCx, JS::Handle<JS::Value> aKey,
    429    JS::Handle<JS::Value> aPrimaryKey, ErrorResult& aRv) {
    430  AssertIsOnOwningThread();
    431 
    432  if (!mTransaction->IsActive()) {
    433    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    434    return;
    435  }
    436 
    437  if (IsSourceDeleted()) {
    438    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    439    return;
    440  }
    441 
    442  if (IsObjectStoreCursor ||
    443      (mDirection != Direction::Next && mDirection != Direction::Prev)) {
    444    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    445    return;
    446  }
    447 
    448  if constexpr (!IsObjectStoreCursor) {
    449    if (!mHaveValue || mContinueCalled) {
    450      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    451      return;
    452    }
    453 
    454    Key key;
    455    auto result = key.SetFromJSVal(aCx, aKey);
    456    if (result.isErr()) {
    457      aRv = result.unwrapErr().ExtractErrorResult(
    458          InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
    459      return;
    460    }
    461 
    462    if (IsLocaleAware() && !key.IsUnset()) {
    463      auto result = key.ToLocaleAwareKey(GetSourceRef().Locale());
    464      if (result.isErr()) {
    465        aRv.Throw(result.inspectErr());
    466        return;
    467      }
    468      key = result.unwrap();
    469    }
    470 
    471    if (key.IsUnset()) {
    472      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    473      return;
    474    }
    475 
    476    Key primaryKey;
    477    result = primaryKey.SetFromJSVal(aCx, aPrimaryKey);
    478    if (result.isErr()) {
    479      aRv = result.unwrapErr().ExtractErrorResult(
    480          InvalidMapsTo<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>);
    481      return;
    482    }
    483 
    484    if (primaryKey.IsUnset()) {
    485      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    486      return;
    487    }
    488 
    489    const Key& sortKey = mData.GetSortKey(IsLocaleAware());
    490 
    491    switch (mDirection) {
    492      case Direction::Next:
    493        if (key < sortKey ||
    494            (key == sortKey && primaryKey <= mData.mObjectStoreKey)) {
    495          aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    496          return;
    497        }
    498        break;
    499 
    500      case Direction::Prev:
    501        if (key > sortKey ||
    502            (key == sortKey && primaryKey >= mData.mObjectStoreKey)) {
    503          aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    504          return;
    505        }
    506        break;
    507 
    508      default:
    509        MOZ_CRASH("Unknown direction type!");
    510    }
    511 
    512    const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
    513    mRequest->SetLoggingSerialNumber(requestSerialNumber);
    514 
    515    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    516        "database(%s).transaction(%s).objectStore(%s)."
    517        "index(%s).cursor(%s).continuePrimaryKey(%s, %s)",
    518        "IDBCursor.continuePrimaryKey(%.0s%.0s%.0s%.0s%.0s%.0s%.0s)",
    519        mTransaction->LoggingSerialNumber(), requestSerialNumber,
    520        IDB_LOG_STRINGIFY(mTransaction->Database()),
    521        IDB_LOG_STRINGIFY(*mTransaction),
    522        IDB_LOG_STRINGIFY(&GetSourceObjectStoreRef()),
    523        IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
    524        IDB_LOG_STRINGIFY(key), IDB_LOG_STRINGIFY(primaryKey));
    525 
    526    GetTypedBackgroundActorRef().SendContinueInternal(
    527        mTransaction->NextRequestId(),
    528        ContinuePrimaryKeyParams(key, primaryKey), mData);
    529 
    530    mContinueCalled = true;
    531  }
    532 }
    533 
    534 template <IDBCursor::Type CursorType>
    535 void IDBTypedCursor<CursorType>::Advance(const uint32_t aCount,
    536                                         ErrorResult& aRv) {
    537  AssertIsOnOwningThread();
    538 
    539  if (!aCount) {
    540    aRv.ThrowTypeError("0 (Zero) is not a valid advance count.");
    541    return;
    542  }
    543 
    544  if (!mTransaction->IsActive()) {
    545    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    546    return;
    547  }
    548 
    549  if (IsSourceDeleted() || !mHaveValue || mContinueCalled) {
    550    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    551    return;
    552  }
    553 
    554  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
    555  mRequest->SetLoggingSerialNumber(requestSerialNumber);
    556 
    557  if constexpr (IsObjectStoreCursor) {
    558    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    559        "database(%s).transaction(%s).objectStore(%s)."
    560        "cursor(%s).advance(%" PRIi32 ")",
    561        "IDBCursor.advance(%.0s%.0s%.0s%.0s%" PRIi32 ")",
    562        mTransaction->LoggingSerialNumber(), requestSerialNumber,
    563        IDB_LOG_STRINGIFY(mTransaction->Database()),
    564        IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(mSource),
    565        IDB_LOG_STRINGIFY(mDirection), aCount);
    566  } else {
    567    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    568        "database(%s).transaction(%s).objectStore(%s)."
    569        "index(%s).cursor(%s).advance(%" PRIi32 ")",
    570        "IDBCursor.advance(%.0s%.0s%.0s%.0s%.0s%" PRIi32 ")",
    571        mTransaction->LoggingSerialNumber(), requestSerialNumber,
    572        IDB_LOG_STRINGIFY(mTransaction->Database()),
    573        IDB_LOG_STRINGIFY(*mTransaction),
    574        IDB_LOG_STRINGIFY(GetSourceRef().ObjectStore()),
    575        IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), aCount);
    576  }
    577 
    578  GetTypedBackgroundActorRef().SendContinueInternal(
    579      mTransaction->NextRequestId(), AdvanceParams(aCount), mData);
    580 
    581  mContinueCalled = true;
    582 }
    583 
    584 template <IDBCursor::Type CursorType>
    585 RefPtr<IDBRequest> IDBTypedCursor<CursorType>::Update(
    586    JSContext* const aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
    587  AssertIsOnOwningThread();
    588 
    589  if (!mTransaction->IsActive()) {
    590    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    591    return nullptr;
    592  }
    593 
    594  if (!mTransaction->IsWriteAllowed()) {
    595    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
    596    return nullptr;
    597  }
    598 
    599  if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
    600      IsSourceDeleted() || !mHaveValue || IsKeyOnlyCursor || mContinueCalled) {
    601    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    602    return nullptr;
    603  }
    604 
    605  if constexpr (!IsKeyOnlyCursor) {
    606    MOZ_ASSERT(!mData.mKey.IsUnset());
    607    if constexpr (!IsObjectStoreCursor) {
    608      MOZ_ASSERT(!mData.mObjectStoreKey.IsUnset());
    609    }
    610 
    611    mTransaction->InvalidateCursorCaches();
    612 
    613    IDBObjectStore::ValueWrapper valueWrapper(aCx, aValue);
    614 
    615    const Key& primaryKey = mData.GetObjectStoreKey();
    616 
    617    RefPtr<IDBRequest> request;
    618 
    619    IDBObjectStore& objectStore = GetSourceObjectStoreRef();
    620    if (objectStore.HasValidKeyPath()) {
    621      if (!valueWrapper.Clone(aCx)) {
    622        aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
    623        return nullptr;
    624      }
    625 
    626      // Make sure the object given has the correct keyPath value set on it.
    627      const KeyPath& keyPath = objectStore.GetKeyPath();
    628      Key key;
    629 
    630      aRv = keyPath.ExtractKey(aCx, valueWrapper.Value(), key);
    631      if (aRv.Failed()) {
    632        return nullptr;
    633      }
    634 
    635      if (key != primaryKey) {
    636        aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
    637        return nullptr;
    638      }
    639 
    640      request = objectStore.AddOrPut(aCx, valueWrapper,
    641                                     /* aKey */ JS::UndefinedHandleValue,
    642                                     /* aOverwrite */ true,
    643                                     /* aFromCursor */ true, aRv);
    644      if (aRv.Failed()) {
    645        return nullptr;
    646      }
    647    } else {
    648      JS::Rooted<JS::Value> keyVal(aCx);
    649      aRv = primaryKey.ToJSVal(aCx, &keyVal);
    650      if (aRv.Failed()) {
    651        return nullptr;
    652      }
    653 
    654      request = objectStore.AddOrPut(aCx, valueWrapper, keyVal,
    655                                     /* aOverwrite */ true,
    656                                     /* aFromCursor */ true, aRv);
    657      if (aRv.Failed()) {
    658        return nullptr;
    659      }
    660    }
    661 
    662    request->SetSource(this);
    663 
    664    if (IsObjectStoreCursor) {
    665      IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    666          "database(%s).transaction(%s).objectStore(%s)."
    667          "cursor(%s).update(%s)",
    668          "IDBCursor.update(%.0s%.0s%.0s%.0s%.0s)",
    669          mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
    670          IDB_LOG_STRINGIFY(mTransaction->Database()),
    671          IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
    672          IDB_LOG_STRINGIFY(mDirection),
    673          IDB_LOG_STRINGIFY(&objectStore, primaryKey));
    674    } else {
    675      IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    676          "database(%s).transaction(%s).objectStore(%s)."
    677          "index(%s).cursor(%s).update(%s)",
    678          "IDBCursor.update(%.0s%.0s%.0s%.0s%.0s%.0s)",
    679          mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
    680          IDB_LOG_STRINGIFY(mTransaction->Database()),
    681          IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
    682          IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
    683          IDB_LOG_STRINGIFY(&objectStore, primaryKey));
    684    }
    685 
    686    return request;
    687  } else {
    688    // XXX: Just to work around a bug in gcc, which otherwise claims 'control
    689    // reaches end of non-void function', which is not true.
    690    return nullptr;
    691  }
    692 }
    693 
    694 template <IDBCursor::Type CursorType>
    695 RefPtr<IDBRequest> IDBTypedCursor<CursorType>::Delete(JSContext* const aCx,
    696                                                      ErrorResult& aRv) {
    697  AssertIsOnOwningThread();
    698 
    699  if (!mTransaction->IsActive()) {
    700    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    701    return nullptr;
    702  }
    703 
    704  if (!mTransaction->IsWriteAllowed()) {
    705    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
    706    return nullptr;
    707  }
    708 
    709  if (IsSourceDeleted() || !mHaveValue || IsKeyOnlyCursor || mContinueCalled) {
    710    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    711    return nullptr;
    712  }
    713 
    714  if constexpr (!IsKeyOnlyCursor) {
    715    MOZ_ASSERT(!mData.mKey.IsUnset());
    716 
    717    mTransaction->InvalidateCursorCaches();
    718 
    719    const Key& primaryKey = mData.GetObjectStoreKey();
    720 
    721    JS::Rooted<JS::Value> key(aCx);
    722    aRv = primaryKey.ToJSVal(aCx, &key);
    723    if (NS_WARN_IF(aRv.Failed())) {
    724      return nullptr;
    725    }
    726 
    727    auto& objectStore = GetSourceObjectStoreRef();
    728    RefPtr<IDBRequest> request =
    729        objectStore.DeleteInternal(aCx, key, /* aFromCursor */ true, aRv);
    730    if (aRv.Failed()) {
    731      return nullptr;
    732    }
    733 
    734    request->SetSource(this);
    735 
    736    if (IsObjectStoreCursor) {
    737      IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    738          "database(%s).transaction(%s).objectStore(%s)."
    739          "cursor(%s).delete(%s)",
    740          "IDBCursor.delete(%.0s%.0s%.0s%.0s%.0s)",
    741          mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
    742          IDB_LOG_STRINGIFY(mTransaction->Database()),
    743          IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
    744          IDB_LOG_STRINGIFY(mDirection),
    745          IDB_LOG_STRINGIFY(&objectStore, primaryKey));
    746    } else {
    747      IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    748          "database(%s).transaction(%s).objectStore(%s)."
    749          "index(%s).cursor(%s).delete(%s)",
    750          "IDBCursor.delete(%.0s%.0s%.0s%.0s%.0s%.0s)",
    751          mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
    752          IDB_LOG_STRINGIFY(mTransaction->Database()),
    753          IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore),
    754          IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection),
    755          IDB_LOG_STRINGIFY(&objectStore, primaryKey));
    756    }
    757 
    758    return request;
    759  } else {
    760    // XXX: Just to work around a bug in gcc, which otherwise claims 'control
    761    // reaches end of non-void function', which is not true.
    762    return nullptr;
    763  }
    764 }
    765 
    766 template <IDBCursor::Type CursorType>
    767 void IDBTypedCursor<CursorType>::Reset(CursorData<CursorType>&& aCursorData) {
    768  this->AssertIsOnOwningThread();
    769 
    770  Reset();
    771 
    772  mData = std::move(aCursorData);
    773 
    774  mHaveValue = !mData.mKey.IsUnset();
    775 }
    776 
    777 template <IDBCursor::Type CursorType>
    778 void IDBTypedCursor<CursorType>::InvalidateCachedResponses() {
    779  AssertIsOnOwningThread();
    780 
    781  // TODO: Can mBackgroundActor actually be empty at this point?
    782  if (mBackgroundActor) {
    783    GetTypedBackgroundActorRef().InvalidateCachedResponses();
    784  }
    785 }
    786 
    787 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor)
    788 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor)
    789 
    790 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor)
    791  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    792  NS_INTERFACE_MAP_ENTRY(nsISupports)
    793 NS_INTERFACE_MAP_END
    794 
    795 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
    796 
    797 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
    798  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
    799 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    800 
    801 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
    802  MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined());
    803  MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey,
    804                tmp->mCachedPrimaryKey.isUndefined());
    805  MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined());
    806 
    807  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    808  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKey)
    809  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedPrimaryKey)
    810  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedValue)
    811 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    812 
    813 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
    814 // Unlinking is done in the subclasses.
    815 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    816 
    817 // Don't unlink mRequest or mSource in
    818 // NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED!
    819 #define NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS_METHODS(_subclassName)    \
    820  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_subclassName, IDBCursor) \
    821    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)                                \
    822  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                                       \
    823                                                                              \
    824  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_subclassName, IDBCursor)   \
    825    NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER                         \
    826    tmp->DropJSObjects();                                                     \
    827  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                         \
    828                                                                              \
    829  NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(_subclassName)                      \
    830  NS_INTERFACE_MAP_END_INHERITING(IDBCursor)                                  \
    831                                                                              \
    832  NS_IMPL_ADDREF_INHERITED(_subclassName, IDBCursor)                          \
    833  NS_IMPL_RELEASE_INHERITED(_subclassName, IDBCursor)
    834 
    835 #define NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(_subclassName) \
    836  NS_IMPL_CYCLE_COLLECTION_CLASS(_subclassName)                    \
    837  NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS_METHODS(_subclassName)
    838 
    839 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBObjectStoreCursor)
    840 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBObjectStoreKeyCursor)
    841 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBIndexCursor)
    842 NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBIndexKeyCursor)
    843 
    844 template <IDBCursor::Type CursorType>
    845 JSObject* IDBTypedCursor<CursorType>::WrapObject(
    846    JSContext* const aCx, JS::Handle<JSObject*> aGivenProto) {
    847  AssertIsOnOwningThread();
    848 
    849  return IsKeyOnlyCursor
    850             ? IDBCursor_Binding::Wrap(aCx, this, aGivenProto)
    851             : IDBCursorWithValue_Binding::Wrap(aCx, this, aGivenProto);
    852 }
    853 
    854 template <IDBCursor::Type CursorType>
    855 template <typename... DataArgs>
    856 IDBTypedCursor<CursorType>::IDBTypedCursor(
    857    indexedDB::BackgroundCursorChild<CursorType>* const aBackgroundActor,
    858    DataArgs&&... aDataArgs)
    859    : IDBCursor{aBackgroundActor},
    860      mData{std::forward<DataArgs>(aDataArgs)...},
    861      mSource(aBackgroundActor->GetSource()) {}
    862 
    863 template <IDBCursor::Type CursorType>
    864 bool IDBTypedCursor<CursorType>::IsLocaleAware() const {
    865  if constexpr (IsObjectStoreCursor) {
    866    return false;
    867  } else {
    868    return !GetSourceRef().Locale().IsEmpty();
    869  }
    870 }
    871 
    872 template class IDBTypedCursor<IDBCursorType::ObjectStore>;
    873 template class IDBTypedCursor<IDBCursorType::ObjectStoreKey>;
    874 template class IDBTypedCursor<IDBCursorType::Index>;
    875 template class IDBTypedCursor<IDBCursorType::IndexKey>;
    876 
    877 }  // namespace mozilla::dom