tor-browser

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

IDBIndex.cpp (19937B)


      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 "IDBIndex.h"
      8 
      9 #include "IDBCursorType.h"
     10 #include "IDBDatabase.h"
     11 #include "IDBEvents.h"
     12 #include "IDBKeyRange.h"
     13 #include "IDBObjectStore.h"
     14 #include "IDBRequest.h"
     15 #include "IDBTransaction.h"
     16 #include "IndexedDatabase.h"
     17 #include "IndexedDatabaseInlines.h"
     18 #include "ProfilerHelpers.h"
     19 #include "ReportInternalError.h"
     20 #include "mozilla/ErrorResult.h"
     21 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.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 mozilla::dom::indexedDB;
     29 
     30 namespace {
     31 
     32 MovingNotNull<RefPtr<IDBRequest>> GenerateRequest(JSContext* aCx,
     33                                                  IDBIndex* aIndex) {
     34  MOZ_ASSERT(aIndex);
     35  aIndex->AssertIsOnOwningThread();
     36 
     37  auto transaction = aIndex->ObjectStore()->AcquireTransaction();
     38  auto* const database = transaction->Database();
     39 
     40  return IDBRequest::Create(aCx, aIndex, database, std::move(transaction));
     41 }
     42 
     43 }  // namespace
     44 
     45 IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata)
     46    : mObjectStore(aObjectStore),
     47      mCachedKeyPath(JS::UndefinedValue()),
     48      mMetadata(aMetadata),
     49      mId(aMetadata->id()),
     50      mRooted(false) {
     51  MOZ_ASSERT(aObjectStore);
     52  aObjectStore->AssertIsOnOwningThread();
     53  MOZ_ASSERT(aMetadata);
     54 }
     55 
     56 IDBIndex::~IDBIndex() {
     57  AssertIsOnOwningThread();
     58 
     59  if (mRooted) {
     60    mozilla::DropJSObjects(this);
     61  }
     62 }
     63 
     64 RefPtr<IDBIndex> IDBIndex::Create(IDBObjectStore* aObjectStore,
     65                                  const IndexMetadata& aMetadata) {
     66  MOZ_ASSERT(aObjectStore);
     67  aObjectStore->AssertIsOnOwningThread();
     68 
     69  return new IDBIndex(aObjectStore, &aMetadata);
     70 }
     71 
     72 #ifdef DEBUG
     73 
     74 void IDBIndex::AssertIsOnOwningThread() const {
     75  MOZ_ASSERT(mObjectStore);
     76  mObjectStore->AssertIsOnOwningThread();
     77 }
     78 
     79 #endif  // DEBUG
     80 
     81 RefPtr<IDBRequest> IDBIndex::OpenCursor(JSContext* aCx,
     82                                        JS::Handle<JS::Value> aRange,
     83                                        IDBCursorDirection aDirection,
     84                                        ErrorResult& aRv) {
     85  AssertIsOnOwningThread();
     86 
     87  return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection,
     88                            aRv);
     89 }
     90 
     91 RefPtr<IDBRequest> IDBIndex::OpenKeyCursor(JSContext* aCx,
     92                                           JS::Handle<JS::Value> aRange,
     93                                           IDBCursorDirection aDirection,
     94                                           ErrorResult& aRv) {
     95  AssertIsOnOwningThread();
     96 
     97  return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, aRv);
     98 }
     99 
    100 RefPtr<IDBRequest> IDBIndex::Get(JSContext* aCx, JS::Handle<JS::Value> aKey,
    101                                 ErrorResult& aRv) {
    102  AssertIsOnOwningThread();
    103 
    104  return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv);
    105 }
    106 
    107 RefPtr<IDBRequest> IDBIndex::GetKey(JSContext* aCx, JS::Handle<JS::Value> aKey,
    108                                    ErrorResult& aRv) {
    109  AssertIsOnOwningThread();
    110 
    111  return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv);
    112 }
    113 
    114 RefPtr<IDBRequest> IDBIndex::GetAll(JSContext* aCx, JS::Handle<JS::Value> aKey,
    115                                    const Optional<uint32_t>& aLimit,
    116                                    ErrorResult& aRv) {
    117  AssertIsOnOwningThread();
    118 
    119  return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv);
    120 }
    121 
    122 RefPtr<IDBRequest> IDBIndex::GetAllKeys(JSContext* aCx,
    123                                        JS::Handle<JS::Value> aKey,
    124                                        const Optional<uint32_t>& aLimit,
    125                                        ErrorResult& aRv) {
    126  AssertIsOnOwningThread();
    127 
    128  return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv);
    129 }
    130 
    131 void IDBIndex::RefreshMetadata(bool aMayDelete) {
    132  AssertIsOnOwningThread();
    133  MOZ_ASSERT_IF(mDeletedMetadata, mMetadata == mDeletedMetadata.get());
    134 
    135  const auto& indexes = mObjectStore->Spec().indexes();
    136  const auto foundIt = std::find_if(
    137      indexes.cbegin(), indexes.cend(),
    138      [id = Id()](const auto& metadata) { return metadata.id() == id; });
    139  const bool found = foundIt != indexes.cend();
    140 
    141  MOZ_ASSERT_IF(!aMayDelete && !mDeletedMetadata, found);
    142 
    143  if (found) {
    144    mMetadata = &*foundIt;
    145    MOZ_ASSERT(mMetadata != mDeletedMetadata.get());
    146    mDeletedMetadata = nullptr;
    147  } else {
    148    NoteDeletion();
    149  }
    150 }
    151 
    152 void IDBIndex::NoteDeletion() {
    153  AssertIsOnOwningThread();
    154  MOZ_ASSERT(mMetadata);
    155  MOZ_ASSERT(Id() == mMetadata->id());
    156 
    157  if (mDeletedMetadata) {
    158    MOZ_ASSERT(mMetadata == mDeletedMetadata.get());
    159    return;
    160  }
    161 
    162  mDeletedMetadata = MakeUnique<IndexMetadata>(*mMetadata);
    163 
    164  mMetadata = mDeletedMetadata.get();
    165 }
    166 
    167 const nsString& IDBIndex::Name() const {
    168  AssertIsOnOwningThread();
    169  MOZ_ASSERT(mMetadata);
    170 
    171  return mMetadata->name();
    172 }
    173 
    174 void IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv) {
    175  AssertIsOnOwningThread();
    176 
    177  const auto& transaction = mObjectStore->TransactionRef();
    178 
    179  if (transaction.GetMode() != IDBTransaction::Mode::VersionChange ||
    180      mDeletedMetadata) {
    181    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    182    return;
    183  }
    184 
    185  if (!transaction.IsActive()) {
    186    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    187    return;
    188  }
    189 
    190  if (aName == mMetadata->name()) {
    191    return;
    192  }
    193 
    194  // Cache logging string of this index before renaming.
    195  const LoggingString loggingOldIndex(this);
    196 
    197  const int64_t indexId = Id();
    198 
    199  nsresult rv =
    200      transaction.Database()->RenameIndex(mObjectStore->Id(), indexId, aName);
    201 
    202  if (NS_FAILED(rv)) {
    203    aRv.Throw(rv);
    204    return;
    205  }
    206 
    207  // Don't do this in the macro because we always need to increment the serial
    208  // number to keep in sync with the parent.
    209  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
    210 
    211  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    212      "database(%s).transaction(%s).objectStore(%s).index(%s)."
    213      "rename(%s)",
    214      "IDBIndex.rename(%.0s%.0s%.0s%.0s%.0s)",
    215      transaction.LoggingSerialNumber(), requestSerialNumber,
    216      IDB_LOG_STRINGIFY(transaction.Database()), IDB_LOG_STRINGIFY(transaction),
    217      IDB_LOG_STRINGIFY(mObjectStore), loggingOldIndex.get(),
    218      IDB_LOG_STRINGIFY(this));
    219 
    220  mObjectStore->MutableTransactionRef().RenameIndex(mObjectStore, indexId,
    221                                                    aName);
    222 }
    223 
    224 bool IDBIndex::Unique() const {
    225  AssertIsOnOwningThread();
    226  MOZ_ASSERT(mMetadata);
    227 
    228  return mMetadata->unique();
    229 }
    230 
    231 bool IDBIndex::MultiEntry() const {
    232  AssertIsOnOwningThread();
    233  MOZ_ASSERT(mMetadata);
    234 
    235  return mMetadata->multiEntry();
    236 }
    237 
    238 bool IDBIndex::LocaleAware() const {
    239  AssertIsOnOwningThread();
    240  MOZ_ASSERT(mMetadata);
    241 
    242  return mMetadata->locale().IsEmpty();
    243 }
    244 
    245 const indexedDB::KeyPath& IDBIndex::GetKeyPath() const {
    246  AssertIsOnOwningThread();
    247  MOZ_ASSERT(mMetadata);
    248 
    249  return mMetadata->keyPath();
    250 }
    251 
    252 void IDBIndex::GetLocale(nsString& aLocale) const {
    253  AssertIsOnOwningThread();
    254  MOZ_ASSERT(mMetadata);
    255 
    256  if (mMetadata->locale().IsEmpty()) {
    257    SetDOMStringToNull(aLocale);
    258  } else {
    259    CopyASCIItoUTF16(mMetadata->locale(), aLocale);
    260  }
    261 }
    262 
    263 const nsCString& IDBIndex::Locale() const {
    264  AssertIsOnOwningThread();
    265  MOZ_ASSERT(mMetadata);
    266 
    267  return mMetadata->locale();
    268 }
    269 
    270 bool IDBIndex::IsAutoLocale() const {
    271  AssertIsOnOwningThread();
    272  MOZ_ASSERT(mMetadata);
    273 
    274  return mMetadata->autoLocale();
    275 }
    276 
    277 nsIGlobalObject* IDBIndex::GetParentObject() const {
    278  AssertIsOnOwningThread();
    279 
    280  return mObjectStore->GetParentObject();
    281 }
    282 
    283 void IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
    284                          ErrorResult& aRv) {
    285  AssertIsOnOwningThread();
    286 
    287  if (!mCachedKeyPath.isUndefined()) {
    288    MOZ_ASSERT(mRooted);
    289    aResult.set(mCachedKeyPath);
    290    return;
    291  }
    292 
    293  MOZ_ASSERT(!mRooted);
    294 
    295  aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
    296  if (NS_WARN_IF(aRv.Failed())) {
    297    return;
    298  }
    299 
    300  if (mCachedKeyPath.isGCThing()) {
    301    mozilla::HoldJSObjects(this);
    302    mRooted = true;
    303  }
    304 
    305  aResult.set(mCachedKeyPath);
    306 }
    307 
    308 RefPtr<IDBRequest> IDBIndex::GetInternal(bool aKeyOnly, JSContext* aCx,
    309                                         JS::Handle<JS::Value> aKey,
    310                                         ErrorResult& aRv) {
    311  AssertIsOnOwningThread();
    312 
    313  if (mDeletedMetadata) {
    314    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    315    return nullptr;
    316  }
    317 
    318  const auto& transaction = mObjectStore->TransactionRef();
    319  if (!transaction.IsActive()) {
    320    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    321    return nullptr;
    322  }
    323 
    324  RefPtr<IDBKeyRange> keyRange;
    325  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
    326  if (NS_WARN_IF(aRv.Failed())) {
    327    return nullptr;
    328  }
    329 
    330  if (!keyRange) {
    331    // Must specify a key or keyRange for get() and getKey().
    332    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR);
    333    return nullptr;
    334  }
    335 
    336  const int64_t objectStoreId = mObjectStore->Id();
    337  const int64_t indexId = Id();
    338 
    339  SerializedKeyRange serializedKeyRange;
    340  keyRange->ToSerialized(serializedKeyRange);
    341 
    342  RequestParams params;
    343 
    344  if (aKeyOnly) {
    345    params = IndexGetKeyParams(objectStoreId, indexId, serializedKeyRange);
    346  } else {
    347    params = IndexGetParams(objectStoreId, indexId, serializedKeyRange);
    348  }
    349 
    350  auto request = GenerateRequest(aCx, this).unwrap();
    351 
    352  if (aKeyOnly) {
    353    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    354        "database(%s).transaction(%s).objectStore(%s).index(%s)."
    355        "getKey(%s)",
    356        "IDBIndex.getKey(%.0s%.0s%.0s%.0s%.0s)",
    357        transaction.LoggingSerialNumber(), request->LoggingSerialNumber(),
    358        IDB_LOG_STRINGIFY(transaction.Database()),
    359        IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    360        IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange));
    361  } else {
    362    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    363        "database(%s).transaction(%s).objectStore(%s).index(%s)."
    364        "get(%s)",
    365        "IDBIndex.get(%.0s%.0s%.0s%.0s%.0s)", transaction.LoggingSerialNumber(),
    366        request->LoggingSerialNumber(),
    367        IDB_LOG_STRINGIFY(transaction.Database()),
    368        IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    369        IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange));
    370  }
    371 
    372  auto& mutableTransaction = mObjectStore->MutableTransactionRef();
    373 
    374  // TODO: This is necessary to preserve request ordering only. Proper
    375  // sequencing of requests should be done in a more sophisticated manner that
    376  // doesn't require invalidating cursor caches (Bug 1580499).
    377  mutableTransaction.InvalidateCursorCaches();
    378 
    379  mutableTransaction.StartRequest(request, params);
    380 
    381  return request;
    382 }
    383 
    384 RefPtr<IDBRequest> IDBIndex::GetAllInternal(bool aKeysOnly, JSContext* aCx,
    385                                            JS::Handle<JS::Value> aKey,
    386                                            const Optional<uint32_t>& aLimit,
    387                                            ErrorResult& aRv) {
    388  AssertIsOnOwningThread();
    389 
    390  if (mDeletedMetadata) {
    391    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    392    return nullptr;
    393  }
    394 
    395  const auto& transaction = mObjectStore->TransactionRef();
    396  if (!transaction.IsActive()) {
    397    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    398    return nullptr;
    399  }
    400 
    401  RefPtr<IDBKeyRange> keyRange;
    402  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
    403  if (NS_WARN_IF(aRv.Failed())) {
    404    return nullptr;
    405  }
    406 
    407  const int64_t objectStoreId = mObjectStore->Id();
    408  const int64_t indexId = Id();
    409 
    410  Maybe<SerializedKeyRange> optionalKeyRange;
    411  if (keyRange) {
    412    SerializedKeyRange serializedKeyRange;
    413    keyRange->ToSerialized(serializedKeyRange);
    414    optionalKeyRange.emplace(serializedKeyRange);
    415  }
    416 
    417  const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
    418 
    419  const auto& params =
    420      aKeysOnly ? RequestParams{IndexGetAllKeysParams(objectStoreId, indexId,
    421                                                      optionalKeyRange, limit)}
    422                : RequestParams{IndexGetAllParams(objectStoreId, indexId,
    423                                                  optionalKeyRange, limit)};
    424 
    425  auto request = GenerateRequest(aCx, this).unwrap();
    426 
    427  if (aKeysOnly) {
    428    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    429        "database(%s).transaction(%s).objectStore(%s).index(%s)."
    430        "getAllKeys(%s, %s)",
    431        "IDBIndex.getAllKeys(%.0s%.0s%.0s%.0s%.0s%.0s)",
    432        transaction.LoggingSerialNumber(), request->LoggingSerialNumber(),
    433        IDB_LOG_STRINGIFY(transaction.Database()),
    434        IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    435        IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
    436        IDB_LOG_STRINGIFY(aLimit));
    437  } else {
    438    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    439        "database(%s).transaction(%s).objectStore(%s).index(%s)."
    440        "getAll(%s, %s)",
    441        "IDBIndex.getAll(%.0s%.0s%.0s%.0s%.0s%.0s)",
    442        transaction.LoggingSerialNumber(), request->LoggingSerialNumber(),
    443        IDB_LOG_STRINGIFY(transaction.Database()),
    444        IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    445        IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
    446        IDB_LOG_STRINGIFY(aLimit));
    447  }
    448 
    449  auto& mutableTransaction = mObjectStore->MutableTransactionRef();
    450 
    451  // TODO: This is necessary to preserve request ordering only. Proper
    452  // sequencing of requests should be done in a more sophisticated manner that
    453  // doesn't require invalidating cursor caches (Bug 1580499).
    454  mutableTransaction.InvalidateCursorCaches();
    455 
    456  mutableTransaction.StartRequest(request, params);
    457 
    458  return request;
    459 }
    460 
    461 RefPtr<IDBRequest> IDBIndex::OpenCursorInternal(bool aKeysOnly, JSContext* aCx,
    462                                                JS::Handle<JS::Value> aRange,
    463                                                IDBCursorDirection aDirection,
    464                                                ErrorResult& aRv) {
    465  AssertIsOnOwningThread();
    466 
    467  if (mDeletedMetadata) {
    468    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    469    return nullptr;
    470  }
    471 
    472  const auto& transaction = mObjectStore->TransactionRef();
    473  if (!transaction.IsActive()) {
    474    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    475    return nullptr;
    476  }
    477 
    478  RefPtr<IDBKeyRange> keyRange;
    479  IDBKeyRange::FromJSVal(aCx, aRange, &keyRange, aRv);
    480  if (NS_WARN_IF(aRv.Failed())) {
    481    return nullptr;
    482  }
    483 
    484  const int64_t objectStoreId = mObjectStore->Id();
    485  const int64_t indexId = Id();
    486 
    487  Maybe<SerializedKeyRange> optionalKeyRange;
    488 
    489  if (keyRange) {
    490    SerializedKeyRange serializedKeyRange;
    491    keyRange->ToSerialized(serializedKeyRange);
    492 
    493    optionalKeyRange.emplace(std::move(serializedKeyRange));
    494  }
    495 
    496  const CommonIndexOpenCursorParams commonIndexParams = {
    497      {objectStoreId, std::move(optionalKeyRange), aDirection}, indexId};
    498 
    499  const auto params =
    500      aKeysOnly ? OpenCursorParams{IndexOpenKeyCursorParams{commonIndexParams}}
    501                : OpenCursorParams{IndexOpenCursorParams{commonIndexParams}};
    502 
    503  auto request = GenerateRequest(aCx, this).unwrap();
    504 
    505  if (aKeysOnly) {
    506    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    507        "database(%s).transaction(%s).objectStore(%s).index(%s)."
    508        "openKeyCursor(%s, %s)",
    509        "IDBIndex.openKeyCursor(%.0s%.0s%.0s%.0s%.0s%.0s)",
    510        transaction.LoggingSerialNumber(), request->LoggingSerialNumber(),
    511        IDB_LOG_STRINGIFY(transaction.Database()),
    512        IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    513        IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
    514        IDB_LOG_STRINGIFY(aDirection));
    515  } else {
    516    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    517        "database(%s).transaction(%s).objectStore(%s).index(%s)."
    518        "openCursor(%s, %s)",
    519        "IDBIndex.openCursor(%.0s%.0s%.0s%.0s%.0s%.0s)",
    520        transaction.LoggingSerialNumber(), request->LoggingSerialNumber(),
    521        IDB_LOG_STRINGIFY(transaction.Database()),
    522        IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    523        IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange),
    524        IDB_LOG_STRINGIFY(aDirection));
    525  }
    526 
    527  const auto actor =
    528      aKeysOnly
    529          ? static_cast<SafeRefPtr<BackgroundCursorChildBase>>(
    530                MakeSafeRefPtr<BackgroundCursorChild<IDBCursorType::IndexKey>>(
    531                    request, this, aDirection))
    532          : MakeSafeRefPtr<BackgroundCursorChild<IDBCursorType::Index>>(
    533                request, this, aDirection);
    534 
    535  auto& mutableTransaction = mObjectStore->MutableTransactionRef();
    536 
    537  // TODO: This is necessary to preserve request ordering only. Proper
    538  // sequencing of requests should be done in a more sophisticated manner that
    539  // doesn't require invalidating cursor caches (Bug 1580499).
    540  mutableTransaction.InvalidateCursorCaches();
    541 
    542  mutableTransaction.OpenCursor(*actor, params);
    543 
    544  return request;
    545 }
    546 
    547 RefPtr<IDBRequest> IDBIndex::Count(JSContext* aCx, JS::Handle<JS::Value> aKey,
    548                                   ErrorResult& aRv) {
    549  AssertIsOnOwningThread();
    550 
    551  if (mDeletedMetadata) {
    552    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    553    return nullptr;
    554  }
    555 
    556  const auto& transaction = mObjectStore->TransactionRef();
    557  if (!transaction.IsActive()) {
    558    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    559    return nullptr;
    560  }
    561 
    562  RefPtr<IDBKeyRange> keyRange;
    563  IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv);
    564  if (aRv.Failed()) {
    565    return nullptr;
    566  }
    567 
    568  IndexCountParams params;
    569  params.objectStoreId() = mObjectStore->Id();
    570  params.indexId() = Id();
    571 
    572  if (keyRange) {
    573    SerializedKeyRange serializedKeyRange;
    574    keyRange->ToSerialized(serializedKeyRange);
    575    params.optionalKeyRange().emplace(serializedKeyRange);
    576  }
    577 
    578  auto request = GenerateRequest(aCx, this).unwrap();
    579 
    580  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    581      "database(%s).transaction(%s).objectStore(%s).index(%s)."
    582      "count(%s)",
    583      "IDBIndex.count(%.0s%.0s%.0s%.0s%.0s)", transaction.LoggingSerialNumber(),
    584      request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction.Database()),
    585      IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore),
    586      IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange));
    587 
    588  auto& mutableTransaction = mObjectStore->MutableTransactionRef();
    589 
    590  // TODO: This is necessary to preserve request ordering only. Proper
    591  // sequencing of requests should be done in a more sophisticated manner that
    592  // doesn't require invalidating cursor caches (Bug 1580499).
    593  mutableTransaction.InvalidateCursorCaches();
    594 
    595  mutableTransaction.StartRequest(request, params);
    596 
    597  return request;
    598 }
    599 
    600 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
    601 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
    602 
    603 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
    604  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    605  NS_INTERFACE_MAP_ENTRY(nsISupports)
    606 NS_INTERFACE_MAP_END
    607 
    608 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
    609 
    610 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex)
    611  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    612  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
    613 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    614 
    615 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
    616  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore)
    617 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    618 
    619 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
    620  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    621 
    622  // Don't unlink mObjectStore!
    623 
    624  tmp->mCachedKeyPath.setUndefined();
    625 
    626  if (tmp->mRooted) {
    627    mozilla::DropJSObjects(tmp);
    628    tmp->mRooted = false;
    629  }
    630 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    631 
    632 JSObject* IDBIndex::WrapObject(JSContext* aCx,
    633                               JS::Handle<JSObject*> aGivenProto) {
    634  return IDBIndex_Binding::Wrap(aCx, this, aGivenProto);
    635 }
    636 
    637 }  // namespace mozilla::dom