зеркало из https://github.com/mozilla/gecko-dev.git
913 строки
26 KiB
C++
913 строки
26 KiB
C++
/* -*- 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/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 {
|
|
namespace dom {
|
|
|
|
using namespace indexedDB;
|
|
|
|
IDBCursor::IDBCursor(Type aType, BackgroundCursorChild* aBackgroundActor,
|
|
Key aKey)
|
|
: mBackgroundActor(aBackgroundActor),
|
|
mRequest(aBackgroundActor->GetRequest()),
|
|
mSourceObjectStore(aBackgroundActor->GetObjectStore()),
|
|
mSourceIndex(aBackgroundActor->GetIndex()),
|
|
mTransaction(mRequest->GetTransaction()),
|
|
mCachedKey(JS::UndefinedValue()),
|
|
mCachedPrimaryKey(JS::UndefinedValue()),
|
|
mCachedValue(JS::UndefinedValue()),
|
|
mKey(std::move(aKey)),
|
|
mType(aType),
|
|
mDirection(aBackgroundActor->GetDirection()),
|
|
mHaveCachedKey(false),
|
|
mHaveCachedPrimaryKey(false),
|
|
mHaveCachedValue(false),
|
|
mRooted(false),
|
|
mContinueCalled(false),
|
|
mHaveValue(true) {
|
|
MOZ_ASSERT(aBackgroundActor);
|
|
aBackgroundActor->AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mRequest);
|
|
MOZ_ASSERT_IF(aType == Type::ObjectStore || aType == Type::ObjectStoreKey,
|
|
mSourceObjectStore);
|
|
MOZ_ASSERT_IF(aType == Type::Index || aType == Type::IndexKey, mSourceIndex);
|
|
MOZ_ASSERT(mTransaction);
|
|
MOZ_ASSERT(!aKey.IsUnset());
|
|
|
|
mTransaction->RegisterCursor(this);
|
|
}
|
|
|
|
bool IDBCursor::IsLocaleAware() const {
|
|
return mSourceIndex && !mSourceIndex->Locale().IsEmpty();
|
|
}
|
|
|
|
IDBCursor::~IDBCursor() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mTransaction->UnregisterCursor(this);
|
|
|
|
DropJSObjects();
|
|
|
|
if (mBackgroundActor) {
|
|
mBackgroundActor->SendDeleteMeInternal();
|
|
MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
|
|
}
|
|
}
|
|
|
|
// static
|
|
RefPtr<IDBCursor> IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
|
|
Key aKey,
|
|
StructuredCloneReadInfo&& aCloneInfo) {
|
|
MOZ_ASSERT(aBackgroundActor);
|
|
aBackgroundActor->AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aBackgroundActor->GetObjectStore());
|
|
MOZ_ASSERT(!aBackgroundActor->GetIndex());
|
|
MOZ_ASSERT(!aKey.IsUnset());
|
|
|
|
RefPtr<IDBCursor> cursor =
|
|
new IDBCursor(Type::ObjectStore, aBackgroundActor, std::move(aKey));
|
|
|
|
cursor->mCloneInfo = std::move(aCloneInfo);
|
|
|
|
return cursor;
|
|
}
|
|
|
|
// static
|
|
RefPtr<IDBCursor> IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
|
|
Key aKey) {
|
|
MOZ_ASSERT(aBackgroundActor);
|
|
aBackgroundActor->AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aBackgroundActor->GetObjectStore());
|
|
MOZ_ASSERT(!aBackgroundActor->GetIndex());
|
|
MOZ_ASSERT(!aKey.IsUnset());
|
|
|
|
RefPtr<IDBCursor> cursor =
|
|
new IDBCursor(Type::ObjectStoreKey, aBackgroundActor, std::move(aKey));
|
|
|
|
return cursor;
|
|
}
|
|
|
|
// static
|
|
RefPtr<IDBCursor> IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
|
|
Key aKey, Key aSortKey, Key aPrimaryKey,
|
|
StructuredCloneReadInfo&& aCloneInfo) {
|
|
MOZ_ASSERT(aBackgroundActor);
|
|
aBackgroundActor->AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aBackgroundActor->GetIndex());
|
|
MOZ_ASSERT(!aBackgroundActor->GetObjectStore());
|
|
MOZ_ASSERT(!aKey.IsUnset());
|
|
MOZ_ASSERT(!aPrimaryKey.IsUnset());
|
|
|
|
RefPtr<IDBCursor> cursor =
|
|
new IDBCursor(Type::Index, aBackgroundActor, std::move(aKey));
|
|
|
|
cursor->mSortKey = std::move(aSortKey);
|
|
cursor->mPrimaryKey = std::move(aPrimaryKey);
|
|
cursor->mCloneInfo = std::move(aCloneInfo);
|
|
|
|
return cursor;
|
|
}
|
|
|
|
// static
|
|
RefPtr<IDBCursor> IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
|
|
Key aKey, Key aSortKey, Key aPrimaryKey) {
|
|
MOZ_ASSERT(aBackgroundActor);
|
|
aBackgroundActor->AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aBackgroundActor->GetIndex());
|
|
MOZ_ASSERT(!aBackgroundActor->GetObjectStore());
|
|
MOZ_ASSERT(!aKey.IsUnset());
|
|
MOZ_ASSERT(!aPrimaryKey.IsUnset());
|
|
|
|
RefPtr<IDBCursor> cursor =
|
|
new IDBCursor(Type::IndexKey, aBackgroundActor, std::move(aKey));
|
|
|
|
cursor->mSortKey = std::move(aSortKey);
|
|
cursor->mPrimaryKey = std::move(aPrimaryKey);
|
|
|
|
return cursor;
|
|
}
|
|
|
|
// static
|
|
auto IDBCursor::ConvertDirection(IDBCursorDirection aDirection) -> Direction {
|
|
switch (aDirection) {
|
|
case mozilla::dom::IDBCursorDirection::Next:
|
|
return Direction::Next;
|
|
|
|
case mozilla::dom::IDBCursorDirection::Nextunique:
|
|
return Direction::NextUnique;
|
|
|
|
case mozilla::dom::IDBCursorDirection::Prev:
|
|
return Direction::Prev;
|
|
|
|
case mozilla::dom::IDBCursorDirection::Prevunique:
|
|
return Direction::PrevUnique;
|
|
|
|
default:
|
|
MOZ_CRASH("Unknown direction!");
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
void IDBCursor::AssertIsOnOwningThread() const {
|
|
MOZ_ASSERT(mTransaction);
|
|
mTransaction->AssertIsOnOwningThread();
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
void IDBCursor::DropJSObjects() {
|
|
AssertIsOnOwningThread();
|
|
|
|
Reset();
|
|
|
|
if (!mRooted) {
|
|
return;
|
|
}
|
|
|
|
mRooted = false;
|
|
|
|
mozilla::DropJSObjects(this);
|
|
}
|
|
|
|
bool IDBCursor::IsSourceDeleted() const {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mTransaction);
|
|
MOZ_ASSERT(mTransaction->CanAcceptRequests());
|
|
|
|
IDBObjectStore* sourceObjectStore;
|
|
if (mType == Type::Index || mType == Type::IndexKey) {
|
|
MOZ_ASSERT(mSourceIndex);
|
|
|
|
if (mSourceIndex->IsDeleted()) {
|
|
return true;
|
|
}
|
|
|
|
sourceObjectStore = mSourceIndex->ObjectStore();
|
|
MOZ_ASSERT(sourceObjectStore);
|
|
} else {
|
|
MOZ_ASSERT(mSourceObjectStore);
|
|
sourceObjectStore = mSourceObjectStore;
|
|
}
|
|
|
|
return sourceObjectStore->IsDeleted();
|
|
}
|
|
|
|
void IDBCursor::Reset() {
|
|
AssertIsOnOwningThread();
|
|
|
|
mCachedKey.setUndefined();
|
|
mCachedPrimaryKey.setUndefined();
|
|
mCachedValue.setUndefined();
|
|
IDBObjectStore::ClearCloneReadInfo(mCloneInfo);
|
|
|
|
mHaveCachedKey = false;
|
|
mHaveCachedPrimaryKey = false;
|
|
mHaveCachedValue = false;
|
|
mHaveValue = false;
|
|
mContinueCalled = false;
|
|
}
|
|
|
|
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!");
|
|
}
|
|
}
|
|
|
|
IDBCursor::Type IDBCursor::GetType() const { return mType; }
|
|
|
|
void IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const {
|
|
AssertIsOnOwningThread();
|
|
|
|
switch (mType) {
|
|
case Type::ObjectStore:
|
|
case Type::ObjectStoreKey:
|
|
MOZ_ASSERT(mSourceObjectStore);
|
|
aSource.SetAsIDBObjectStore() = mSourceObjectStore;
|
|
return;
|
|
|
|
case Type::Index:
|
|
case Type::IndexKey:
|
|
MOZ_ASSERT(mSourceIndex);
|
|
aSource.SetAsIDBIndex() = mSourceIndex;
|
|
return;
|
|
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Bad type!");
|
|
}
|
|
}
|
|
|
|
void IDBCursor::GetKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
|
ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue);
|
|
|
|
if (!mHaveValue) {
|
|
aResult.setUndefined();
|
|
return;
|
|
}
|
|
|
|
if (!mHaveCachedKey) {
|
|
if (!mRooted) {
|
|
mozilla::HoldJSObjects(this);
|
|
mRooted = true;
|
|
}
|
|
|
|
aRv = mKey.ToJSVal(aCx, mCachedKey);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
mHaveCachedKey = true;
|
|
}
|
|
|
|
aResult.set(mCachedKey);
|
|
}
|
|
|
|
void IDBCursor::GetPrimaryKey(JSContext* 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 =
|
|
(mType == Type::ObjectStore || mType == Type::ObjectStoreKey)
|
|
? mKey
|
|
: mPrimaryKey;
|
|
|
|
MOZ_ASSERT(!key.IsUnset());
|
|
|
|
aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
mHaveCachedPrimaryKey = true;
|
|
}
|
|
|
|
aResult.set(mCachedPrimaryKey);
|
|
}
|
|
|
|
void IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
|
ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mType == Type::ObjectStore || mType == Type::Index);
|
|
|
|
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, mCloneInfo, &val))) {
|
|
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
return;
|
|
}
|
|
|
|
IDBObjectStore::ClearCloneReadInfo(mCloneInfo);
|
|
|
|
mCachedValue = val;
|
|
mHaveCachedValue = true;
|
|
}
|
|
|
|
aResult.set(mCachedValue);
|
|
}
|
|
|
|
void IDBCursor::Continue(JSContext* aCx, JS::Handle<JS::Value> aKey,
|
|
ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
|
|
if (!mTransaction->CanAcceptRequests()) {
|
|
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, aRv);
|
|
if (!result.Is(Ok, aRv)) {
|
|
if (result.Is(Invalid, aRv)) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (IsLocaleAware() && !key.IsUnset()) {
|
|
Key tmp;
|
|
result = key.ToLocaleAwareKey(tmp, mSourceIndex->Locale(), aRv);
|
|
if (!result.Is(Ok, aRv)) {
|
|
if (result.Is(Invalid, aRv)) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
}
|
|
return;
|
|
}
|
|
key = tmp;
|
|
}
|
|
|
|
const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
|
|
|
|
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 (mType == Type::ObjectStore || mType == Type::ObjectStoreKey) {
|
|
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"cursor(%s).continue(%s)",
|
|
"IDBCursor.continue()", mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(mSourceObjectStore),
|
|
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()", mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()),
|
|
IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection),
|
|
IDB_LOG_STRINGIFY(key));
|
|
}
|
|
|
|
mBackgroundActor->SendContinueInternal(ContinueParams(key), mKey,
|
|
mPrimaryKey);
|
|
|
|
mContinueCalled = true;
|
|
}
|
|
|
|
void IDBCursor::ContinuePrimaryKey(JSContext* aCx, JS::Handle<JS::Value> aKey,
|
|
JS::Handle<JS::Value> aPrimaryKey,
|
|
ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
|
|
if (!mTransaction->CanAcceptRequests()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return;
|
|
}
|
|
|
|
if (IsSourceDeleted()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return;
|
|
}
|
|
|
|
if ((mType != Type::Index && mType != Type::IndexKey) ||
|
|
(mDirection != Direction::Next && mDirection != Direction::Prev)) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
|
return;
|
|
}
|
|
|
|
if (!mHaveValue || mContinueCalled) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return;
|
|
}
|
|
|
|
Key key;
|
|
auto result = key.SetFromJSVal(aCx, aKey, aRv);
|
|
if (!result.Is(Ok, aRv)) {
|
|
if (result.Is(Invalid, aRv)) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (IsLocaleAware() && !key.IsUnset()) {
|
|
Key tmp;
|
|
result = key.ToLocaleAwareKey(tmp, mSourceIndex->Locale(), aRv);
|
|
if (!result.Is(Ok, aRv)) {
|
|
if (result.Is(Invalid, aRv)) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
}
|
|
return;
|
|
}
|
|
key = tmp;
|
|
}
|
|
|
|
if (key.IsUnset()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
return;
|
|
}
|
|
|
|
Key primaryKey;
|
|
result = primaryKey.SetFromJSVal(aCx, aPrimaryKey, aRv);
|
|
if (!result.Is(Ok, aRv)) {
|
|
if (result.Is(Invalid, aRv)) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (primaryKey.IsUnset()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
return;
|
|
}
|
|
|
|
const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
|
|
|
|
switch (mDirection) {
|
|
case Direction::Next:
|
|
if (key < sortKey || (key == sortKey && primaryKey <= mPrimaryKey)) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case Direction::Prev:
|
|
if (key > sortKey || (key == sortKey && primaryKey >= mPrimaryKey)) {
|
|
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()", mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()),
|
|
IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection),
|
|
IDB_LOG_STRINGIFY(key), IDB_LOG_STRINGIFY(primaryKey));
|
|
|
|
mBackgroundActor->SendContinueInternal(
|
|
ContinuePrimaryKeyParams(key, primaryKey), mKey, mPrimaryKey);
|
|
|
|
mContinueCalled = true;
|
|
}
|
|
|
|
void IDBCursor::Advance(uint32_t aCount, ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
|
|
if (!aCount) {
|
|
aRv.ThrowTypeError(u"0 (Zero) is not a valid advance count.");
|
|
return;
|
|
}
|
|
|
|
if (!mTransaction->CanAcceptRequests()) {
|
|
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 (mType == Type::ObjectStore || mType == Type::ObjectStoreKey) {
|
|
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"cursor(%s).advance(%ld)",
|
|
"IDBCursor.advance()", mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(mSourceObjectStore),
|
|
IDB_LOG_STRINGIFY(mDirection), aCount);
|
|
} else {
|
|
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"index(%s).cursor(%s).advance(%ld)",
|
|
"IDBCursor.advance()", mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber, IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()),
|
|
IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection), aCount);
|
|
}
|
|
|
|
mBackgroundActor->SendContinueInternal(AdvanceParams(aCount), mKey,
|
|
mPrimaryKey);
|
|
|
|
mContinueCalled = true;
|
|
}
|
|
|
|
RefPtr<IDBRequest> IDBCursor::Update(JSContext* aCx,
|
|
JS::Handle<JS::Value> aValue,
|
|
ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
|
|
if (!mTransaction->CanAcceptRequests()) {
|
|
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 || mType == Type::ObjectStoreKey ||
|
|
mType == Type::IndexKey || mContinueCalled) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(mType == Type::ObjectStore || mType == Type::Index);
|
|
MOZ_ASSERT(!mKey.IsUnset());
|
|
MOZ_ASSERT_IF(mType == Type::Index, !mPrimaryKey.IsUnset());
|
|
|
|
mTransaction->InvalidateCursorCaches();
|
|
|
|
IDBObjectStore* objectStore;
|
|
if (mType == Type::ObjectStore) {
|
|
objectStore = mSourceObjectStore;
|
|
} else {
|
|
objectStore = mSourceIndex->ObjectStore();
|
|
}
|
|
|
|
MOZ_ASSERT(objectStore);
|
|
|
|
IDBObjectStore::ValueWrapper valueWrapper(aCx, aValue);
|
|
|
|
const Key& primaryKey = (mType == Type::ObjectStore) ? mKey : mPrimaryKey;
|
|
|
|
RefPtr<IDBRequest> request;
|
|
|
|
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 (mType == Type::ObjectStore) {
|
|
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"cursor(%s).update(%s)",
|
|
"IDBCursor.update()", 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()", mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(objectStore),
|
|
IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection),
|
|
IDB_LOG_STRINGIFY(objectStore, primaryKey));
|
|
}
|
|
|
|
return request;
|
|
}
|
|
|
|
RefPtr<IDBRequest> IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) {
|
|
AssertIsOnOwningThread();
|
|
|
|
if (!mTransaction->CanAcceptRequests()) {
|
|
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 || mType == Type::ObjectStoreKey ||
|
|
mType == Type::IndexKey || mContinueCalled) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(mType == Type::ObjectStore || mType == Type::Index);
|
|
MOZ_ASSERT(!mKey.IsUnset());
|
|
|
|
mTransaction->InvalidateCursorCaches();
|
|
|
|
IDBObjectStore* const objectStore = mType == Type::ObjectStore
|
|
? mSourceObjectStore.get()
|
|
: mSourceIndex->ObjectStore();
|
|
|
|
MOZ_ASSERT(objectStore);
|
|
|
|
const Key& primaryKey = (mType == Type::ObjectStore) ? mKey : mPrimaryKey;
|
|
|
|
JS::Rooted<JS::Value> key(aCx);
|
|
aRv = primaryKey.ToJSVal(aCx, &key);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDBRequest> request =
|
|
objectStore->DeleteInternal(aCx, key, /* aFromCursor */ true, aRv);
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
request->SetSource(this);
|
|
|
|
if (mType == Type::ObjectStore) {
|
|
IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"cursor(%s).delete(%s)",
|
|
"IDBCursor.delete()", 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()", mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction), IDB_LOG_STRINGIFY(objectStore),
|
|
IDB_LOG_STRINGIFY(mSourceIndex), IDB_LOG_STRINGIFY(mDirection),
|
|
IDB_LOG_STRINGIFY(objectStore, primaryKey));
|
|
}
|
|
|
|
return request;
|
|
}
|
|
|
|
void IDBCursor::Reset(Key&& aKey, StructuredCloneReadInfo&& aValue) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mType == Type::ObjectStore);
|
|
|
|
Reset();
|
|
|
|
mKey = std::move(aKey);
|
|
mCloneInfo = std::move(aValue);
|
|
|
|
mHaveValue = !mKey.IsUnset();
|
|
}
|
|
|
|
void IDBCursor::Reset(Key&& aKey) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mType == Type::ObjectStoreKey);
|
|
|
|
Reset();
|
|
|
|
mKey = std::move(aKey);
|
|
|
|
mHaveValue = !mKey.IsUnset();
|
|
}
|
|
|
|
void IDBCursor::Reset(Key&& aKey, Key&& aSortKey, Key&& aPrimaryKey,
|
|
StructuredCloneReadInfo&& aValue) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mType == Type::Index);
|
|
|
|
Reset();
|
|
|
|
mKey = std::move(aKey);
|
|
mSortKey = std::move(aSortKey);
|
|
mPrimaryKey = std::move(aPrimaryKey);
|
|
mCloneInfo = std::move(aValue);
|
|
|
|
mHaveValue = !mKey.IsUnset();
|
|
}
|
|
|
|
void IDBCursor::Reset(Key&& aKey, Key&& aSortKey, Key&& aPrimaryKey) {
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mType == Type::IndexKey);
|
|
|
|
Reset();
|
|
|
|
mKey = std::move(aKey);
|
|
mSortKey = std::move(aSortKey);
|
|
mPrimaryKey = std::move(aPrimaryKey);
|
|
|
|
mHaveValue = !mKey.IsUnset();
|
|
}
|
|
|
|
void IDBCursor::InvalidateCachedResponses() {
|
|
AssertIsOnOwningThread();
|
|
|
|
// TODO: In what case would mBackgroundActor be nullptr?
|
|
if (mBackgroundActor) {
|
|
mBackgroundActor->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(mSourceObjectStore)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceIndex)
|
|
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)
|
|
// Don't unlink mRequest, mSourceObjectStore, or mSourceIndex!
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
tmp->DropJSObjects();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
JSObject* IDBCursor::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
AssertIsOnOwningThread();
|
|
|
|
switch (mType) {
|
|
case Type::ObjectStore:
|
|
case Type::Index:
|
|
return IDBCursorWithValue_Binding::Wrap(aCx, this, aGivenProto);
|
|
|
|
case Type::ObjectStoreKey:
|
|
case Type::IndexKey:
|
|
return IDBCursor_Binding::Wrap(aCx, this, aGivenProto);
|
|
|
|
default:
|
|
MOZ_CRASH("Bad type!");
|
|
}
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|