diff options
Diffstat (limited to 'dom/indexedDB/IDBCursor.cpp')
-rw-r--r-- | dom/indexedDB/IDBCursor.cpp | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp new file mode 100644 index 0000000000..2fb6e98715 --- /dev/null +++ b/dom/indexedDB/IDBCursor.cpp @@ -0,0 +1,875 @@ +/* -*- 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 <IDBCursor::Type CursorType> +IDBTypedCursor<CursorType>::~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<IDBObjectStoreCursor> IDBCursor::Create( + BackgroundCursorChild<Type::ObjectStore>* const aBackgroundActor, Key aKey, + StructuredCloneReadInfoChild&& aCloneInfo) { + MOZ_ASSERT(aBackgroundActor); + aBackgroundActor->AssertIsOnOwningThread(); + MOZ_ASSERT(!aKey.IsUnset()); + + return MakeRefPtr<IDBObjectStoreCursor>(aBackgroundActor, std::move(aKey), + std::move(aCloneInfo)); +} + +// static +RefPtr<IDBObjectStoreKeyCursor> IDBCursor::Create( + BackgroundCursorChild<Type::ObjectStoreKey>* const aBackgroundActor, + Key aKey) { + MOZ_ASSERT(aBackgroundActor); + aBackgroundActor->AssertIsOnOwningThread(); + MOZ_ASSERT(!aKey.IsUnset()); + + return MakeRefPtr<IDBObjectStoreKeyCursor>(aBackgroundActor, std::move(aKey)); +} + +// static +RefPtr<IDBIndexCursor> IDBCursor::Create( + BackgroundCursorChild<Type::Index>* 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<IDBIndexCursor>(aBackgroundActor, std::move(aKey), + std::move(aSortKey), std::move(aPrimaryKey), + std::move(aCloneInfo)); +} + +// static +RefPtr<IDBIndexKeyCursor> IDBCursor::Create( + BackgroundCursorChild<Type::IndexKey>* const aBackgroundActor, Key aKey, + Key aSortKey, Key aPrimaryKey) { + MOZ_ASSERT(aBackgroundActor); + aBackgroundActor->AssertIsOnOwningThread(); + MOZ_ASSERT(!aKey.IsUnset()); + MOZ_ASSERT(!aPrimaryKey.IsUnset()); + + return MakeRefPtr<IDBIndexKeyCursor>(aBackgroundActor, std::move(aKey), + std::move(aSortKey), + std::move(aPrimaryKey)); +} + +#ifdef DEBUG + +void IDBCursor::AssertIsOnOwningThread() const { + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +template <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::DropJSObjects() { + AssertIsOnOwningThread(); + + Reset(); + + if (!mRooted) { + return; + } + + mRooted = false; + + mozilla::DropJSObjects(this); +} + +template <IDBCursor::Type CursorType> +bool IDBTypedCursor<CursorType>::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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::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<IDBRequest> IDBCursor::Request() const { + AssertIsOnOwningThread(); + return mRequest; +} + +template <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::GetSource( + OwningIDBObjectStoreOrIDBIndex& aSource) const { + AssertIsOnOwningThread(); + + if constexpr (IsObjectStoreCursor) { + aSource.SetAsIDBObjectStore() = mSource; + } else { + aSource.SetAsIDBIndex() = mSource; + } +} + +template <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::GetKey(JSContext* const aCx, + JS::MutableHandle<JS::Value> 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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::GetPrimaryKey( + JSContext* const aCx, JS::MutableHandle<JS::Value> 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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::GetValue(JSContext* const aCx, + JS::MutableHandle<JS::Value> aResult, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + if constexpr (!IsKeyOnlyCursor) { + if (!mHaveValue) { + aResult.setUndefined(); + return; + } + + if (!mHaveCachedValue) { + if (!mRooted) { + mozilla::HoldJSObjects(this); + mRooted = true; + } + + JS::Rooted<JS::Value> 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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::Continue(JSContext* const aCx, + JS::Handle<JS::Value> 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<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>); + 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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::ContinuePrimaryKey( + JSContext* const aCx, JS::Handle<JS::Value> aKey, + JS::Handle<JS::Value> 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<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>); + 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<NS_ERROR_DOM_INDEXEDDB_DATA_ERR>); + 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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::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 <IDBCursor::Type CursorType> +RefPtr<IDBRequest> IDBTypedCursor<CursorType>::Update( + JSContext* const aCx, JS::Handle<JS::Value> 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<IDBRequest> 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<JS::Value> 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 <IDBCursor::Type CursorType> +RefPtr<IDBRequest> IDBTypedCursor<CursorType>::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<JS::Value> key(aCx); + aRv = primaryKey.ToJSVal(aCx, &key); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + auto& objectStore = GetSourceObjectStoreRef(); + RefPtr<IDBRequest> 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 <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::Reset(CursorData<CursorType>&& aCursorData) { + this->AssertIsOnOwningThread(); + + Reset(); + + mData = std::move(aCursorData); + + mHaveValue = !mData.mKey.IsUnset(); +} + +template <IDBCursor::Type CursorType> +void IDBTypedCursor<CursorType>::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 <IDBCursor::Type CursorType> +JSObject* IDBTypedCursor<CursorType>::WrapObject( + JSContext* const aCx, JS::Handle<JSObject*> aGivenProto) { + AssertIsOnOwningThread(); + + return IsKeyOnlyCursor + ? IDBCursor_Binding::Wrap(aCx, this, aGivenProto) + : IDBCursorWithValue_Binding::Wrap(aCx, this, aGivenProto); +} + +template <IDBCursor::Type CursorType> +template <typename... DataArgs> +IDBTypedCursor<CursorType>::IDBTypedCursor( + indexedDB::BackgroundCursorChild<CursorType>* const aBackgroundActor, + DataArgs&&... aDataArgs) + : IDBCursor{aBackgroundActor}, + mData{std::forward<DataArgs>(aDataArgs)...}, + mSource(aBackgroundActor->GetSource()) {} + +template <IDBCursor::Type CursorType> +bool IDBTypedCursor<CursorType>::IsLocaleAware() const { + if constexpr (IsObjectStoreCursor) { + return false; + } else { + return !GetSourceRef().Locale().IsEmpty(); + } +} + +template class IDBTypedCursor<IDBCursorType::ObjectStore>; +template class IDBTypedCursor<IDBCursorType::ObjectStoreKey>; +template class IDBTypedCursor<IDBCursorType::Index>; +template class IDBTypedCursor<IDBCursorType::IndexKey>; + +} // namespace mozilla::dom |