Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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(
mTransaction->NextRequestId(), 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(
mTransaction->NextRequestId(),
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(
mTransaction->NextRequestId(), 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