summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/IDBCursor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/IDBCursor.cpp')
-rw-r--r--dom/indexedDB/IDBCursor.cpp1006
1 files changed, 1006 insertions, 0 deletions
diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp
new file mode 100644
index 000000000..e5d8913f9
--- /dev/null
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -0,0 +1,1006 @@
+/* -*- 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,
+ const Key& aKey)
+ : mBackgroundActor(aBackgroundActor)
+ , mRequest(aBackgroundActor->GetRequest())
+ , mSourceObjectStore(aBackgroundActor->GetObjectStore())
+ , mSourceIndex(aBackgroundActor->GetIndex())
+ , mTransaction(mRequest->GetTransaction())
+ , mScriptOwner(mTransaction->Database()->GetScriptOwner())
+ , mCachedKey(JS::UndefinedValue())
+ , mCachedPrimaryKey(JS::UndefinedValue())
+ , mCachedValue(JS::UndefinedValue())
+ , mKey(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());
+ MOZ_ASSERT(mScriptOwner);
+
+ if (mScriptOwner) {
+ mozilla::HoldJSObjects(this);
+ mRooted = true;
+ }
+}
+
+#ifdef ENABLE_INTL_API
+bool
+IDBCursor::IsLocaleAware() const
+{
+ return mSourceIndex && !mSourceIndex->Locale().IsEmpty();
+}
+#endif
+
+IDBCursor::~IDBCursor()
+{
+ AssertIsOnOwningThread();
+
+ DropJSObjects();
+
+ if (mBackgroundActor) {
+ mBackgroundActor->SendDeleteMeInternal();
+ MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+ }
+}
+
+// static
+already_AddRefed<IDBCursor>
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
+ const 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, aKey);
+
+ cursor->mCloneInfo = Move(aCloneInfo);
+
+ return cursor.forget();
+}
+
+// static
+already_AddRefed<IDBCursor>
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
+ const 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, aKey);
+
+ return cursor.forget();
+}
+
+// static
+already_AddRefed<IDBCursor>
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
+ const Key& aKey,
+ const Key& aSortKey,
+ const 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, aKey);
+
+ cursor->mSortKey = Move(aSortKey);
+ cursor->mPrimaryKey = Move(aPrimaryKey);
+ cursor->mCloneInfo = Move(aCloneInfo);
+
+ return cursor.forget();
+}
+
+// static
+already_AddRefed<IDBCursor>
+IDBCursor::Create(BackgroundCursorChild* aBackgroundActor,
+ const Key& aKey,
+ const Key& aSortKey,
+ const 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, aKey);
+
+ cursor->mSortKey = Move(aSortKey);
+ cursor->mPrimaryKey = Move(aPrimaryKey);
+
+ return cursor.forget();
+}
+
+// static
+auto
+IDBCursor::ConvertDirection(IDBCursorDirection aDirection) -> Direction
+{
+ switch (aDirection) {
+ case mozilla::dom::IDBCursorDirection::Next:
+ return NEXT;
+
+ case mozilla::dom::IDBCursorDirection::Nextunique:
+ return NEXT_UNIQUE;
+
+ case mozilla::dom::IDBCursorDirection::Prev:
+ return PREV;
+
+ case mozilla::dom::IDBCursorDirection::Prevunique:
+ return PREV_UNIQUE;
+
+ 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;
+ }
+
+ mScriptOwner = nullptr;
+ mRooted = false;
+
+ mozilla::DropJSObjects(this);
+}
+
+bool
+IDBCursor::IsSourceDeleted() const
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mTransaction->IsOpen());
+
+ 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;
+}
+
+nsPIDOMWindowInner*
+IDBCursor::GetParentObject() const
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mTransaction);
+
+ return mTransaction->GetParentObject();
+}
+
+IDBCursorDirection
+IDBCursor::GetDirection() const
+{
+ AssertIsOnOwningThread();
+
+ switch (mDirection) {
+ case NEXT:
+ return IDBCursorDirection::Next;
+
+ case NEXT_UNIQUE:
+ return IDBCursorDirection::Nextunique;
+
+ case PREV:
+ return IDBCursorDirection::Prev;
+
+ case PREV_UNIQUE:
+ return IDBCursorDirection::Prevunique;
+
+ default:
+ MOZ_CRASH("Bad direction!");
+ }
+}
+
+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->IsOpen()) {
+ 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;
+ aRv = key.SetFromJSVal(aCx, aKey);
+ if (aRv.Failed()) {
+ return;
+ }
+
+#ifdef ENABLE_INTL_API
+ if (IsLocaleAware() && !key.IsUnset()) {
+ Key tmp;
+ aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
+ if (aRv.Failed()) {
+ return;
+ }
+ key = tmp;
+ }
+
+ const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
+#else
+ const Key& sortKey = mKey;
+#endif
+
+ if (!key.IsUnset()) {
+ switch (mDirection) {
+ case NEXT:
+ case NEXT_UNIQUE:
+ if (key <= sortKey) {
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+ return;
+ }
+ break;
+
+ case PREV:
+ case PREV_UNIQUE:
+ 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "cursor(%s).continue(%s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continue()",
+ IDB_LOG_ID_STRING(),
+ 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "index(%s).cursor(%s).continue(%s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continue()",
+ IDB_LOG_ID_STRING(),
+ 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));
+
+ mContinueCalled = true;
+}
+
+void
+IDBCursor::ContinuePrimaryKey(JSContext* aCx,
+ JS::Handle<JS::Value> aKey,
+ JS::Handle<JS::Value> aPrimaryKey,
+ ErrorResult &aRv)
+{
+ AssertIsOnOwningThread();
+
+ if (!mTransaction->IsOpen()) {
+ 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 != NEXT && mDirection != 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;
+ aRv = key.SetFromJSVal(aCx, aKey);
+ if (aRv.Failed()) {
+ return;
+ }
+
+#ifdef ENABLE_INTL_API
+ if (IsLocaleAware() && !key.IsUnset()) {
+ Key tmp;
+ aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
+ if (aRv.Failed()) {
+ return;
+ }
+ key = tmp;
+ }
+
+ const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
+#else
+ const Key& sortKey = mKey;
+#endif
+
+ if (key.IsUnset()) {
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+ return;
+ }
+
+ Key primaryKey;
+ aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ if (primaryKey.IsUnset()) {
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+ return;
+ }
+
+ switch (mDirection) {
+ case NEXT:
+ if (key < sortKey ||
+ (key == sortKey && primaryKey <= mPrimaryKey)) {
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+ return;
+ }
+ break;
+
+ case 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "index(%s).cursor(%s).continuePrimaryKey(%s, %s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continuePrimaryKey()",
+ IDB_LOG_ID_STRING(),
+ 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));
+
+ mContinueCalled = true;
+}
+
+void
+IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
+{
+ AssertIsOnOwningThread();
+
+ if (!aCount) {
+ aRv.ThrowTypeError<MSG_INVALID_ADVANCE_COUNT>();
+ return;
+ }
+
+ if (!mTransaction->IsOpen()) {
+ 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "cursor(%s).advance(%ld)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.advance()",
+ IDB_LOG_ID_STRING(),
+ 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "index(%s).cursor(%s).advance(%ld)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.advance()",
+ IDB_LOG_ID_STRING(),
+ 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));
+
+ mContinueCalled = true;
+}
+
+already_AddRefed<IDBRequest>
+IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv)
+{
+ AssertIsOnOwningThread();
+
+ if (!mTransaction->IsOpen()) {
+ 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::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());
+
+ IDBObjectStore* objectStore;
+ if (mType == Type_ObjectStore) {
+ objectStore = mSourceObjectStore;
+ } else {
+ objectStore = mSourceIndex->ObjectStore();
+ }
+
+ MOZ_ASSERT(objectStore);
+
+ const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey;
+
+ RefPtr<IDBRequest> request;
+
+ if (objectStore->HasValidKeyPath()) {
+ // Make sure the object given has the correct keyPath value set on it.
+ const KeyPath& keyPath = objectStore->GetKeyPath();
+ Key key;
+
+ aRv = keyPath.ExtractKey(aCx, aValue, key);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ if (key != primaryKey) {
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+ return nullptr;
+ }
+
+ request = objectStore->AddOrPut(aCx,
+ aValue,
+ /* 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,
+ aValue,
+ keyVal,
+ /* aOverwrite */ true,
+ /* aFromCursor */ true,
+ aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ }
+
+ request->SetSource(this);
+
+ if (mType == Type_ObjectStore) {
+ IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "cursor(%s).update(%s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.update()",
+ IDB_LOG_ID_STRING(),
+ 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "index(%s).cursor(%s).update(%s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.update()",
+ IDB_LOG_ID_STRING(),
+ 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.forget();
+}
+
+already_AddRefed<IDBRequest>
+IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
+{
+ AssertIsOnOwningThread();
+
+ if (!mTransaction->IsOpen()) {
+ 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());
+
+ IDBObjectStore* objectStore;
+ if (mType == Type_ObjectStore) {
+ objectStore = mSourceObjectStore;
+ } else {
+ objectStore = 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "cursor(%s).delete(%s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.delete()",
+ IDB_LOG_ID_STRING(),
+ 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("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "database(%s).transaction(%s).objectStore(%s)."
+ "index(%s).cursor(%s).delete(%s)",
+ "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.delete()",
+ IDB_LOG_ID_STRING(),
+ 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.forget();
+}
+
+void
+IDBCursor::Reset(Key&& aKey, StructuredCloneReadInfo&& aValue)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mType == Type_ObjectStore);
+
+ Reset();
+
+ mKey = Move(aKey);
+ mCloneInfo = Move(aValue);
+
+ mHaveValue = !mKey.IsUnset();
+}
+
+void
+IDBCursor::Reset(Key&& aKey)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mType == Type_ObjectStoreKey);
+
+ Reset();
+
+ mKey = Move(aKey);
+
+ mHaveValue = !mKey.IsUnset();
+}
+
+void
+IDBCursor::Reset(Key&& aKey,
+ Key&& aSortKey,
+ Key&& aPrimaryKey,
+ StructuredCloneReadInfo&& aValue)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mType == Type_Index);
+
+ Reset();
+
+ mKey = Move(aKey);
+ mSortKey = Move(aSortKey);
+ mPrimaryKey = Move(aPrimaryKey);
+ mCloneInfo = Move(aValue);
+
+ mHaveValue = !mKey.IsUnset();
+}
+
+void
+IDBCursor::Reset(Key&& aKey,
+ Key&& aSortKey,
+ Key&& aPrimaryKey)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mType == Type_IndexKey);
+
+ Reset();
+
+ mKey = Move(aKey);
+ mSortKey = Move(aSortKey);
+ mPrimaryKey = Move(aPrimaryKey);
+
+ mHaveValue = !mKey.IsUnset();
+}
+
+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_SCRIPT_OBJECTS
+ 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(mScriptOwner)
+ 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 IDBCursorWithValueBinding::Wrap(aCx, this, aGivenProto);
+
+ case Type_ObjectStoreKey:
+ case Type_IndexKey:
+ return IDBCursorBinding::Wrap(aCx, this, aGivenProto);
+
+ default:
+ MOZ_CRASH("Bad type!");
+ }
+}
+
+} // namespace dom
+} // namespace mozilla