/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "IDBCursor.h" #include "IDBDatabase.h" #include "IDBIndex.h" #include "IDBObjectStore.h" #include "IDBRequest.h" #include "IDBTransaction.h" #include "IndexedDatabaseInlines.h" #include "mozilla/ErrorResult.h" #include "mozilla/HoldDropJSObjects.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "nsString.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" // Include this last to avoid path problems on Windows. #include "ActorsChild.h" namespace mozilla::dom { using namespace indexedDB; IDBCursor::IDBCursor(BackgroundCursorChildBase* const aBackgroundActor) : mBackgroundActor(WrapNotNull(aBackgroundActor)), mRequest(aBackgroundActor->GetRequest()), mTransaction(&mRequest->MutableTransactionRef()), mCachedKey(JS::UndefinedValue()), mCachedPrimaryKey(JS::UndefinedValue()), mCachedValue(JS::UndefinedValue()), mDirection(aBackgroundActor->GetDirection()), mHaveCachedKey(false), mHaveCachedPrimaryKey(false), mHaveCachedValue(false), mRooted(false), mContinueCalled(false), mHaveValue(true) { MOZ_ASSERT(aBackgroundActor); aBackgroundActor->AssertIsOnOwningThread(); MOZ_ASSERT(mRequest); mTransaction->RegisterCursor(*this); } template IDBTypedCursor::~IDBTypedCursor() { AssertIsOnOwningThread(); mTransaction->UnregisterCursor(*this); DropJSObjects(); if (mBackgroundActor) { (*mBackgroundActor)->SendDeleteMeInternal(); MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); } // Let's explicitly not leave any dangling CheckedUnsafePtr. mTransaction = nullptr; } // static RefPtr IDBCursor::Create( BackgroundCursorChild* const aBackgroundActor, Key aKey, StructuredCloneReadInfoChild&& aCloneInfo) { MOZ_ASSERT(aBackgroundActor); aBackgroundActor->AssertIsOnOwningThread(); MOZ_ASSERT(!aKey.IsUnset()); return MakeRefPtr(aBackgroundActor, std::move(aKey), std::move(aCloneInfo)); } // static RefPtr IDBCursor::Create( BackgroundCursorChild* const aBackgroundActor, Key aKey) { MOZ_ASSERT(aBackgroundActor); aBackgroundActor->AssertIsOnOwningThread(); MOZ_ASSERT(!aKey.IsUnset()); return MakeRefPtr(aBackgroundActor, std::move(aKey)); } // static RefPtr IDBCursor::Create( BackgroundCursorChild* const aBackgroundActor, Key aKey, Key aSortKey, Key aPrimaryKey, StructuredCloneReadInfoChild&& aCloneInfo) { MOZ_ASSERT(aBackgroundActor); aBackgroundActor->AssertIsOnOwningThread(); MOZ_ASSERT(!aKey.IsUnset()); MOZ_ASSERT(!aPrimaryKey.IsUnset()); return MakeRefPtr(aBackgroundActor, std::move(aKey), std::move(aSortKey), std::move(aPrimaryKey), std::move(aCloneInfo)); } // static RefPtr IDBCursor::Create( BackgroundCursorChild* const aBackgroundActor, Key aKey, Key aSortKey, Key aPrimaryKey) { MOZ_ASSERT(aBackgroundActor); aBackgroundActor->AssertIsOnOwningThread(); MOZ_ASSERT(!aKey.IsUnset()); MOZ_ASSERT(!aPrimaryKey.IsUnset()); return MakeRefPtr(aBackgroundActor, std::move(aKey), std::move(aSortKey), std::move(aPrimaryKey)); } #ifdef DEBUG void IDBCursor::AssertIsOnOwningThread() const { MOZ_ASSERT(mTransaction); mTransaction->AssertIsOnOwningThread(); } #endif // DEBUG template void IDBTypedCursor::DropJSObjects() { AssertIsOnOwningThread(); Reset(); if (!mRooted) { return; } mRooted = false; mozilla::DropJSObjects(this); } template bool IDBTypedCursor::IsSourceDeleted() const { AssertIsOnOwningThread(); MOZ_ASSERT(mTransaction); MOZ_ASSERT(mTransaction->IsActive()); const auto* const sourceObjectStore = [this]() -> const IDBObjectStore* { if constexpr (IsObjectStoreCursor) { return mSource; } else { if (GetSourceRef().IsDeleted()) { return nullptr; } const auto* const res = GetSourceRef().ObjectStore(); MOZ_ASSERT(res); return res; } }(); return !sourceObjectStore || sourceObjectStore->IsDeleted(); } void IDBCursor::ResetBase() { AssertIsOnOwningThread(); mCachedKey.setUndefined(); mCachedPrimaryKey.setUndefined(); mCachedValue.setUndefined(); mHaveCachedKey = false; mHaveCachedPrimaryKey = false; mHaveCachedValue = false; mHaveValue = false; mContinueCalled = false; } template void IDBTypedCursor::Reset() { AssertIsOnOwningThread(); if constexpr (!IsKeyOnlyCursor) { IDBObjectStore::ClearCloneReadInfo(mData.mCloneInfo); } ResetBase(); } nsIGlobalObject* IDBCursor::GetParentObject() const { AssertIsOnOwningThread(); MOZ_ASSERT(mTransaction); return mTransaction->GetParentObject(); } IDBCursorDirection IDBCursor::GetDirection() const { AssertIsOnOwningThread(); switch (mDirection) { case Direction::Next: return IDBCursorDirection::Next; case Direction::Nextunique: return IDBCursorDirection::Nextunique; case Direction::Prev: return IDBCursorDirection::Prev; case Direction::Prevunique: return IDBCursorDirection::Prevunique; default: MOZ_CRASH("Bad direction!"); } } RefPtr IDBCursor::Request() const { AssertIsOnOwningThread(); return mRequest; } template void IDBTypedCursor::GetSource( OwningIDBObjectStoreOrIDBIndex& aSource) const { AssertIsOnOwningThread(); if constexpr (IsObjectStoreCursor) { aSource.SetAsIDBObjectStore() = mSource; } else { aSource.SetAsIDBIndex() = mSource; } } template void IDBTypedCursor::GetKey(JSContext* const aCx, JS::MutableHandle aResult, ErrorResult& aRv) { AssertIsOnOwningThread(); MOZ_ASSERT(!mData.mKey.IsUnset() || !mHaveValue); if (!mHaveValue) { aResult.setUndefined(); return; } if (!mHaveCachedKey) { if (!mRooted) { mozilla::HoldJSObjects(this); mRooted = true; } aRv = mData.mKey.ToJSVal(aCx, mCachedKey); if (NS_WARN_IF(aRv.Failed())) { return; } mHaveCachedKey = true; } aResult.set(mCachedKey); } template void IDBTypedCursor::GetPrimaryKey( JSContext* const aCx, JS::MutableHandle aResult, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mHaveValue) { aResult.setUndefined(); return; } if (!mHaveCachedPrimaryKey) { if (!mRooted) { mozilla::HoldJSObjects(this); mRooted = true; } const Key& key = mData.GetObjectStoreKey(); MOZ_ASSERT(!key.IsUnset()); aRv = key.ToJSVal(aCx, mCachedPrimaryKey); if (NS_WARN_IF(aRv.Failed())) { return; } mHaveCachedPrimaryKey = true; } aResult.set(mCachedPrimaryKey); } template void IDBTypedCursor::GetValue(JSContext* const aCx, JS::MutableHandle aResult, ErrorResult& aRv) { AssertIsOnOwningThread(); if constexpr (!IsKeyOnlyCursor) { if (!mHaveValue) { aResult.setUndefined(); return; } if (!mHaveCachedValue) { if (!mRooted) { mozilla::HoldJSObjects(this); mRooted = true; } JS::Rooted val(aCx); if (NS_WARN_IF(!IDBObjectStore::DeserializeValue( aCx, std::move(mData.mCloneInfo), &val))) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } // XXX This seems redundant, sine mData.mCloneInfo is moved above. IDBObjectStore::ClearCloneReadInfo(mData.mCloneInfo); mCachedValue = val; mHaveCachedValue = true; } aResult.set(mCachedValue); } else { MOZ_CRASH("This shouldn't be callable on a key-only cursor."); } } template void IDBTypedCursor::Continue(JSContext* const aCx, JS::Handle aKey, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsActive()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (IsSourceDeleted() || !mHaveValue || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } Key key; auto result = key.SetFromJSVal(aCx, aKey); if (result.isErr()) { aRv = result.unwrapErr().ExtractErrorResult( InvalidMapsTo); return; } if constexpr (!IsObjectStoreCursor) { if (IsLocaleAware() && !key.IsUnset()) { auto result = key.ToLocaleAwareKey(GetSourceRef().Locale()); if (result.isErr()) { aRv.Throw(result.inspectErr()); return; } key = result.unwrap(); } } const Key& sortKey = mData.GetSortKey(IsLocaleAware()); if (!key.IsUnset()) { switch (mDirection) { case Direction::Next: case Direction::Nextunique: if (key <= sortKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; case Direction::Prev: case Direction::Prevunique: if (key >= sortKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; default: MOZ_CRASH("Unknown direction type!"); } } const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); mRequest->SetLoggingSerialNumber(requestSerialNumber); if constexpr (IsObjectStoreCursor) { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).continue(%s)", "IDBCursor.continue(%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key)); } else { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).continue(%s)", "IDBCursor.continue(%.0s%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(GetSourceRef().ObjectStore()), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key)); } GetTypedBackgroundActorRef().SendContinueInternal(ContinueParams(key), mData); mContinueCalled = true; } template void IDBTypedCursor::ContinuePrimaryKey( JSContext* const aCx, JS::Handle aKey, JS::Handle aPrimaryKey, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsActive()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (IsSourceDeleted()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } if (IsObjectStoreCursor || (mDirection != Direction::Next && mDirection != Direction::Prev)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if constexpr (!IsObjectStoreCursor) { if (!mHaveValue || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } Key key; auto result = key.SetFromJSVal(aCx, aKey); if (result.isErr()) { aRv = result.unwrapErr().ExtractErrorResult( InvalidMapsTo); return; } if (IsLocaleAware() && !key.IsUnset()) { auto result = key.ToLocaleAwareKey(GetSourceRef().Locale()); if (result.isErr()) { aRv.Throw(result.inspectErr()); return; } key = result.unwrap(); } if (key.IsUnset()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } Key primaryKey; result = primaryKey.SetFromJSVal(aCx, aPrimaryKey); if (result.isErr()) { aRv = result.unwrapErr().ExtractErrorResult( InvalidMapsTo); return; } if (primaryKey.IsUnset()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } const Key& sortKey = mData.GetSortKey(IsLocaleAware()); switch (mDirection) { case Direction::Next: if (key < sortKey || (key == sortKey && primaryKey <= mData.mObjectStoreKey)) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; case Direction::Prev: if (key > sortKey || (key == sortKey && primaryKey >= mData.mObjectStoreKey)) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; default: MOZ_CRASH("Unknown direction type!"); } const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); mRequest->SetLoggingSerialNumber(requestSerialNumber); IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).continuePrimaryKey(%s, %s)", "IDBCursor.continuePrimaryKey(%.0s%.0s%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&GetSourceObjectStoreRef()), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(key), IDB_LOG_STRINGIFY(primaryKey)); GetTypedBackgroundActorRef().SendContinueInternal( ContinuePrimaryKeyParams(key, primaryKey), mData); mContinueCalled = true; } } template void IDBTypedCursor::Advance(const uint32_t aCount, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!aCount) { aRv.ThrowTypeError("0 (Zero) is not a valid advance count."); return; } if (!mTransaction->IsActive()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return; } if (IsSourceDeleted() || !mHaveValue || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return; } const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); mRequest->SetLoggingSerialNumber(requestSerialNumber); if constexpr (IsObjectStoreCursor) { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).advance(%" PRIi32 ")", "IDBCursor.advance(%.0s%.0s%.0s%.0s%" PRIi32 ")", mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), aCount); } else { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).advance(%" PRIi32 ")", "IDBCursor.advance(%.0s%.0s%.0s%.0s%.0s%" PRIi32 ")", mTransaction->LoggingSerialNumber(), requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(GetSourceRef().ObjectStore()), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), aCount); } GetTypedBackgroundActorRef().SendContinueInternal(AdvanceParams(aCount), mData); mContinueCalled = true; } template RefPtr IDBTypedCursor::Update( JSContext* const aCx, JS::Handle aValue, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsActive()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup || IsSourceDeleted() || !mHaveValue || IsKeyOnlyCursor || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } if constexpr (!IsKeyOnlyCursor) { MOZ_ASSERT(!mData.mKey.IsUnset()); if constexpr (!IsObjectStoreCursor) { MOZ_ASSERT(!mData.mObjectStoreKey.IsUnset()); } mTransaction->InvalidateCursorCaches(); IDBObjectStore::ValueWrapper valueWrapper(aCx, aValue); const Key& primaryKey = mData.GetObjectStoreKey(); RefPtr request; IDBObjectStore& objectStore = GetSourceObjectStoreRef(); if (objectStore.HasValidKeyPath()) { if (!valueWrapper.Clone(aCx)) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return nullptr; } // Make sure the object given has the correct keyPath value set on it. const KeyPath& keyPath = objectStore.GetKeyPath(); Key key; aRv = keyPath.ExtractKey(aCx, valueWrapper.Value(), key); if (aRv.Failed()) { return nullptr; } if (key != primaryKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return nullptr; } request = objectStore.AddOrPut(aCx, valueWrapper, /* aKey */ JS::UndefinedHandleValue, /* aOverwrite */ true, /* aFromCursor */ true, aRv); if (aRv.Failed()) { return nullptr; } } else { JS::Rooted keyVal(aCx); aRv = primaryKey.ToJSVal(aCx, &keyVal); if (aRv.Failed()) { return nullptr; } request = objectStore.AddOrPut(aCx, valueWrapper, keyVal, /* aOverwrite */ true, /* aFromCursor */ true, aRv); if (aRv.Failed()) { return nullptr; } } request->SetSource(this); if (IsObjectStoreCursor) { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).update(%s)", "IDBCursor.update(%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(&objectStore, primaryKey)); } else { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).update(%s)", "IDBCursor.update(%.0s%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(&objectStore, primaryKey)); } return request; } else { // XXX: Just to work around a bug in gcc, which otherwise claims 'control // reaches end of non-void function', which is not true. return nullptr; } } template RefPtr IDBTypedCursor::Delete(JSContext* const aCx, ErrorResult& aRv) { AssertIsOnOwningThread(); if (!mTransaction->IsActive()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } if (IsSourceDeleted() || !mHaveValue || IsKeyOnlyCursor || mContinueCalled) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } if constexpr (!IsKeyOnlyCursor) { MOZ_ASSERT(!mData.mKey.IsUnset()); mTransaction->InvalidateCursorCaches(); const Key& primaryKey = mData.GetObjectStoreKey(); JS::Rooted key(aCx); aRv = primaryKey.ToJSVal(aCx, &key); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } auto& objectStore = GetSourceObjectStoreRef(); RefPtr request = objectStore.DeleteInternal(aCx, key, /* aFromCursor */ true, aRv); if (aRv.Failed()) { return nullptr; } request->SetSource(this); if (IsObjectStoreCursor) { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).delete(%s)", "IDBCursor.delete(%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(&objectStore, primaryKey)); } else { IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).delete(%s)", "IDBCursor.delete(%.0s%.0s%.0s%.0s%.0s%.0s)", mTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(mTransaction->Database()), IDB_LOG_STRINGIFY(*mTransaction), IDB_LOG_STRINGIFY(&objectStore), IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), IDB_LOG_STRINGIFY(&objectStore, primaryKey)); } return request; } else { // XXX: Just to work around a bug in gcc, which otherwise claims 'control // reaches end of non-void function', which is not true. return nullptr; } } template void IDBTypedCursor::Reset(CursorData&& aCursorData) { this->AssertIsOnOwningThread(); Reset(); mData = std::move(aCursorData); mHaveValue = !mData.mKey.IsUnset(); } template void IDBTypedCursor::InvalidateCachedResponses() { AssertIsOnOwningThread(); // TODO: Can mBackgroundActor actually be empty at this point? if (mBackgroundActor) { GetTypedBackgroundActorRef().InvalidateCachedResponses(); } } NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor) NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined()); MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey, tmp->mCachedPrimaryKey.isUndefined()); MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined()); NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKey) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedPrimaryKey) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedValue) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) // Unlinking is done in the subclasses. NS_IMPL_CYCLE_COLLECTION_UNLINK_END // Don't unlink mRequest or mSource in // NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED! #define NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS_METHODS(_subclassName) \ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_subclassName, IDBCursor) \ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource) \ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \ \ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_subclassName, IDBCursor) \ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ tmp->DropJSObjects(); \ NS_IMPL_CYCLE_COLLECTION_UNLINK_END \ \ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(_subclassName) \ NS_INTERFACE_MAP_END_INHERITING(IDBCursor) \ \ NS_IMPL_ADDREF_INHERITED(_subclassName, IDBCursor) \ NS_IMPL_RELEASE_INHERITED(_subclassName, IDBCursor) #define NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(_subclassName) \ NS_IMPL_CYCLE_COLLECTION_CLASS(_subclassName) \ NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS_METHODS(_subclassName) NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBObjectStoreCursor) NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBObjectStoreKeyCursor) NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBIndexCursor) NS_IMPL_CYCLE_COLLECTION_IDBCURSOR_SUBCLASS(IDBIndexKeyCursor) template JSObject* IDBTypedCursor::WrapObject( JSContext* const aCx, JS::Handle aGivenProto) { AssertIsOnOwningThread(); return IsKeyOnlyCursor ? IDBCursor_Binding::Wrap(aCx, this, aGivenProto) : IDBCursorWithValue_Binding::Wrap(aCx, this, aGivenProto); } template template IDBTypedCursor::IDBTypedCursor( indexedDB::BackgroundCursorChild* const aBackgroundActor, DataArgs&&... aDataArgs) : IDBCursor{aBackgroundActor}, mData{std::forward(aDataArgs)...}, mSource(aBackgroundActor->GetSource()) {} template bool IDBTypedCursor::IsLocaleAware() const { if constexpr (IsObjectStoreCursor) { return false; } else { return !GetSourceRef().Locale().IsEmpty(); } } template class IDBTypedCursor; template class IDBTypedCursor; template class IDBTypedCursor; template class IDBTypedCursor; } // namespace mozilla::dom