forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			995 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			995 lines
		
	
	
	
		
			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,
 | |
|                      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;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| IDBCursor::IsLocaleAware() const
 | |
| {
 | |
|   return mSourceIndex && !mSourceIndex->Locale().IsEmpty();
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   }
 | |
| 
 | |
|   if (IsLocaleAware() && !key.IsUnset()) {
 | |
|     Key tmp;
 | |
|     aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|     key = tmp;
 | |
|   }
 | |
| 
 | |
|   const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   if (IsLocaleAware() && !key.IsUnset()) {
 | |
|     Key tmp;
 | |
|     aRv = key.ToLocaleBasedKey(tmp, mSourceIndex->Locale());
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|     key = tmp;
 | |
|   }
 | |
| 
 | |
|   const Key& sortKey = IsLocaleAware() ? mSortKey : mKey;
 | |
| 
 | |
|   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(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
 | 
