michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "base/basictypes.h" michael@0: michael@0: #include "IDBCursor.h" michael@0: michael@0: #include "mozilla/storage.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "AsyncConnectionHelper.h" michael@0: #include "IDBEvents.h" michael@0: #include "IDBIndex.h" michael@0: #include "IDBObjectStore.h" michael@0: #include "IDBTransaction.h" michael@0: #include "ProfilerHelpers.h" michael@0: #include "ReportInternalError.h" michael@0: #include "TransactionThreadPool.h" michael@0: michael@0: #include "ipc/IndexedDBChild.h" michael@0: #include "ipc/IndexedDBParent.h" michael@0: michael@0: #include "IndexedDatabaseInlines.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: #include "mozilla/dom/UnionTypes.h" michael@0: michael@0: USING_INDEXEDDB_NAMESPACE michael@0: using namespace mozilla::dom::indexedDB::ipc; michael@0: using mozilla::dom::Optional; michael@0: using mozilla::dom::OwningIDBObjectStoreOrIDBIndex; michael@0: using mozilla::ErrorResult; michael@0: michael@0: static_assert(sizeof(size_t) >= sizeof(IDBCursor::Direction), michael@0: "Relying on conversion between size_t and IDBCursor::Direction"); michael@0: michael@0: namespace { michael@0: michael@0: class CursorHelper : public AsyncConnectionHelper michael@0: { michael@0: public: michael@0: CursorHelper(IDBCursor* aCursor) michael@0: : AsyncConnectionHelper(aCursor->Transaction(), aCursor->Request()), michael@0: mCursor(aCursor), mActor(nullptr) michael@0: { michael@0: NS_ASSERTION(aCursor, "Null cursor!"); michael@0: } michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(CursorRequestParams& aParams) = 0; michael@0: michael@0: virtual nsresult michael@0: UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; michael@0: michael@0: protected: michael@0: virtual ~CursorHelper() michael@0: { } michael@0: michael@0: nsRefPtr mCursor; michael@0: michael@0: private: michael@0: IndexedDBCursorRequestChild* mActor; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: BEGIN_INDEXEDDB_NAMESPACE michael@0: michael@0: class ContinueHelper : public CursorHelper michael@0: { michael@0: public: michael@0: ContinueHelper(IDBCursor* aCursor, michael@0: int32_t aCount) michael@0: : CursorHelper(aCursor), mCount(aCount) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aCursor); michael@0: MOZ_ASSERT(aCount > 0); michael@0: } michael@0: michael@0: virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(CursorRequestParams& aParams) MOZ_OVERRIDE; michael@0: michael@0: virtual ChildProcessSendResult michael@0: SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) michael@0: MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: virtual ~ContinueHelper() michael@0: { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: } michael@0: michael@0: virtual nsresult michael@0: BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0; michael@0: michael@0: virtual nsresult michael@0: GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0; michael@0: michael@0: void UpdateCursorState() michael@0: { michael@0: mCursor->mCachedKey = JSVAL_VOID; michael@0: mCursor->mCachedPrimaryKey = JSVAL_VOID; michael@0: mCursor->mCachedValue = JSVAL_VOID; michael@0: mCursor->mHaveCachedKey = false; michael@0: mCursor->mHaveCachedPrimaryKey = false; michael@0: mCursor->mHaveCachedValue = false; michael@0: mCursor->mContinueCalled = false; michael@0: michael@0: if (mKey.IsUnset()) { michael@0: mCursor->mHaveValue = false; michael@0: } else { michael@0: MOZ_ASSERT(mCursor->mType == IDBCursor::OBJECTSTORE || michael@0: mCursor->mType == IDBCursor::OBJECTSTOREKEY || michael@0: !mObjectKey.IsUnset()); michael@0: michael@0: // Set new values. michael@0: mCursor->mKey = mKey; michael@0: mCursor->mObjectKey = mObjectKey; michael@0: mCursor->mContinueToKey.Unset(); michael@0: michael@0: mCursor->mCloneReadInfo = Move(mCloneReadInfo); michael@0: mCloneReadInfo.mCloneBuffer.clear(); michael@0: } michael@0: } michael@0: michael@0: int32_t mCount; michael@0: Key mKey; michael@0: Key mObjectKey; michael@0: StructuredCloneReadInfo mCloneReadInfo; michael@0: }; michael@0: michael@0: class ContinueObjectStoreHelper : public ContinueHelper michael@0: { michael@0: public: michael@0: ContinueObjectStoreHelper(IDBCursor* aCursor, michael@0: uint32_t aCount) michael@0: : ContinueHelper(aCursor, aCount) michael@0: { } michael@0: michael@0: protected: michael@0: virtual ~ContinueObjectStoreHelper() michael@0: { } michael@0: michael@0: private: michael@0: nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement); michael@0: nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); michael@0: }; michael@0: michael@0: class ContinueObjectStoreKeyHelper : public ContinueObjectStoreHelper michael@0: { michael@0: public: michael@0: ContinueObjectStoreKeyHelper(IDBCursor* aCursor, michael@0: uint32_t aCount) michael@0: : ContinueObjectStoreHelper(aCursor, aCount) michael@0: { } michael@0: michael@0: private: michael@0: virtual ~ContinueObjectStoreKeyHelper() michael@0: { } michael@0: michael@0: virtual nsresult michael@0: GatherResultsFromStatement(mozIStorageStatement* aStatement) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: class ContinueIndexHelper : public ContinueHelper michael@0: { michael@0: public: michael@0: ContinueIndexHelper(IDBCursor* aCursor, michael@0: uint32_t aCount) michael@0: : ContinueHelper(aCursor, aCount) michael@0: { } michael@0: michael@0: protected: michael@0: virtual ~ContinueIndexHelper() michael@0: { } michael@0: michael@0: private: michael@0: nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement); michael@0: nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); michael@0: }; michael@0: michael@0: class ContinueIndexObjectHelper : public ContinueIndexHelper michael@0: { michael@0: public: michael@0: ContinueIndexObjectHelper(IDBCursor* aCursor, michael@0: uint32_t aCount) michael@0: : ContinueIndexHelper(aCursor, aCount) michael@0: { } michael@0: michael@0: private: michael@0: virtual ~ContinueIndexObjectHelper() michael@0: { } michael@0: michael@0: nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); michael@0: }; michael@0: michael@0: END_INDEXEDDB_NAMESPACE michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBCursor::Create(IDBRequest* aRequest, michael@0: IDBTransaction* aTransaction, michael@0: IDBObjectStore* aObjectStore, michael@0: Direction aDirection, michael@0: const Key& aRangeKey, michael@0: const nsACString& aContinueQuery, michael@0: const nsACString& aContinueToQuery, michael@0: const Key& aKey, michael@0: StructuredCloneReadInfo&& aCloneReadInfo) michael@0: { michael@0: NS_ASSERTION(aObjectStore, "Null pointer!"); michael@0: NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, michael@0: aRangeKey, aContinueQuery, aContinueToQuery); michael@0: NS_ASSERTION(cursor, "This shouldn't fail!"); michael@0: michael@0: cursor->mObjectStore = aObjectStore; michael@0: cursor->mType = OBJECTSTORE; michael@0: cursor->mKey = aKey; michael@0: cursor->mCloneReadInfo = Move(aCloneReadInfo); michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBCursor::Create(IDBRequest* aRequest, michael@0: IDBTransaction* aTransaction, michael@0: IDBObjectStore* aObjectStore, michael@0: Direction aDirection, michael@0: const Key& aRangeKey, michael@0: const nsACString& aContinueQuery, michael@0: const nsACString& aContinueToQuery, michael@0: const Key& aKey) michael@0: { michael@0: MOZ_ASSERT(aObjectStore); michael@0: MOZ_ASSERT(!aKey.IsUnset()); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, michael@0: aRangeKey, aContinueQuery, aContinueToQuery); michael@0: NS_ASSERTION(cursor, "This shouldn't fail!"); michael@0: michael@0: cursor->mObjectStore = aObjectStore; michael@0: cursor->mType = OBJECTSTOREKEY; michael@0: cursor->mKey = aKey; michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBCursor::Create(IDBRequest* aRequest, michael@0: IDBTransaction* aTransaction, michael@0: IDBIndex* aIndex, michael@0: Direction aDirection, michael@0: const Key& aRangeKey, michael@0: const nsACString& aContinueQuery, michael@0: const nsACString& aContinueToQuery, michael@0: const Key& aKey, michael@0: const Key& aObjectKey) michael@0: { michael@0: NS_ASSERTION(aIndex, "Null pointer!"); michael@0: NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); michael@0: NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!"); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), michael@0: aDirection, aRangeKey, aContinueQuery, michael@0: aContinueToQuery); michael@0: NS_ASSERTION(cursor, "This shouldn't fail!"); michael@0: michael@0: cursor->mIndex = aIndex; michael@0: cursor->mType = INDEXKEY; michael@0: cursor->mKey = aKey, michael@0: cursor->mObjectKey = aObjectKey; michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBCursor::Create(IDBRequest* aRequest, michael@0: IDBTransaction* aTransaction, michael@0: IDBIndex* aIndex, michael@0: Direction aDirection, michael@0: const Key& aRangeKey, michael@0: const nsACString& aContinueQuery, michael@0: const nsACString& aContinueToQuery, michael@0: const Key& aKey, michael@0: const Key& aObjectKey, michael@0: StructuredCloneReadInfo&& aCloneReadInfo) michael@0: { michael@0: NS_ASSERTION(aIndex, "Null pointer!"); michael@0: NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), michael@0: aDirection, aRangeKey, aContinueQuery, michael@0: aContinueToQuery); michael@0: NS_ASSERTION(cursor, "This shouldn't fail!"); michael@0: michael@0: cursor->mObjectStore = aIndex->ObjectStore(); michael@0: cursor->mIndex = aIndex; michael@0: cursor->mType = INDEXOBJECT; michael@0: cursor->mKey = aKey; michael@0: cursor->mObjectKey = aObjectKey; michael@0: cursor->mCloneReadInfo = Move(aCloneReadInfo); michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: // static michael@0: IDBCursor::Direction michael@0: IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection) michael@0: { michael@0: switch (aDirection) { michael@0: case mozilla::dom::IDBCursorDirection::Next: michael@0: return NEXT; michael@0: michael@0: case mozilla::dom::IDBCursorDirection::Nextunique: michael@0: return NEXT_UNIQUE; michael@0: michael@0: case mozilla::dom::IDBCursorDirection::Prev: michael@0: return PREV; michael@0: michael@0: case mozilla::dom::IDBCursorDirection::Prevunique: michael@0: return PREV_UNIQUE; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown direction!"); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBCursor::CreateCommon(IDBRequest* aRequest, michael@0: IDBTransaction* aTransaction, michael@0: IDBObjectStore* aObjectStore, michael@0: Direction aDirection, michael@0: const Key& aRangeKey, michael@0: const nsACString& aContinueQuery, michael@0: const nsACString& aContinueToQuery) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aRequest, "Null pointer!"); michael@0: NS_ASSERTION(aTransaction, "Null pointer!"); michael@0: NS_ASSERTION(aObjectStore, "Null pointer!"); michael@0: NS_ASSERTION(!aContinueQuery.IsEmpty() || michael@0: !IndexedDatabaseManager::IsMainProcess(), michael@0: "Empty query!"); michael@0: NS_ASSERTION(!aContinueToQuery.IsEmpty() || michael@0: !IndexedDatabaseManager::IsMainProcess(), michael@0: "Empty query!"); michael@0: michael@0: nsRefPtr cursor = new IDBCursor(); michael@0: michael@0: IDBDatabase* database = aTransaction->Database(); michael@0: cursor->mScriptOwner = database->GetScriptOwner(); michael@0: michael@0: if (cursor->mScriptOwner) { michael@0: mozilla::HoldJSObjects(cursor.get()); michael@0: cursor->mRooted = true; michael@0: } michael@0: michael@0: cursor->mRequest = aRequest; michael@0: cursor->mTransaction = aTransaction; michael@0: cursor->mObjectStore = aObjectStore; michael@0: cursor->mDirection = aDirection; michael@0: cursor->mContinueQuery = aContinueQuery; michael@0: cursor->mContinueToQuery = aContinueToQuery; michael@0: cursor->mRangeKey = aRangeKey; michael@0: michael@0: return cursor.forget(); michael@0: } michael@0: michael@0: IDBCursor::IDBCursor() michael@0: : mScriptOwner(nullptr), michael@0: mType(OBJECTSTORE), michael@0: mDirection(IDBCursor::NEXT), michael@0: mCachedKey(JSVAL_VOID), michael@0: mCachedPrimaryKey(JSVAL_VOID), michael@0: mCachedValue(JSVAL_VOID), michael@0: mActorChild(nullptr), michael@0: mActorParent(nullptr), michael@0: mHaveCachedKey(false), michael@0: mHaveCachedPrimaryKey(false), michael@0: mHaveCachedValue(false), michael@0: mRooted(false), michael@0: mContinueCalled(false), michael@0: mHaveValue(true) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: IDBCursor::~IDBCursor() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); michael@0: if (mActorChild) { michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: mActorChild->Send__delete__(mActorChild); michael@0: NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); michael@0: } michael@0: michael@0: DropJSObjects(); michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: } michael@0: michael@0: void michael@0: IDBCursor::DropJSObjects() michael@0: { michael@0: if (!mRooted) { michael@0: return; michael@0: } michael@0: mScriptOwner = nullptr; michael@0: mCachedKey = JSVAL_VOID; michael@0: mCachedPrimaryKey = JSVAL_VOID; michael@0: mCachedValue = JSVAL_VOID; michael@0: mHaveCachedKey = false; michael@0: mHaveCachedPrimaryKey = false; michael@0: mHaveCachedValue = false; michael@0: mRooted = false; michael@0: mHaveValue = false; michael@0: mozilla::DropJSObjects(this); michael@0: } michael@0: michael@0: void michael@0: IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aCount > 0); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return; michael@0: } michael@0: michael@0: if (!mHaveValue || mContinueCalled) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: return; michael@0: } michael@0: michael@0: mContinueToKey = aKey; michael@0: michael@0: MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done); michael@0: michael@0: mRequest->Reset(); michael@0: michael@0: nsRefPtr helper; michael@0: switch (mType) { michael@0: case OBJECTSTORE: michael@0: helper = new ContinueObjectStoreHelper(this, aCount); michael@0: break; michael@0: michael@0: case OBJECTSTOREKEY: michael@0: helper = new ContinueObjectStoreKeyHelper(this, aCount); michael@0: break; michael@0: michael@0: case INDEXKEY: michael@0: helper = new ContinueIndexHelper(this, aCount); michael@0: break; michael@0: michael@0: case INDEXOBJECT: michael@0: helper = new ContinueIndexObjectHelper(this, aCount); michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown cursor type!"); michael@0: } michael@0: michael@0: nsresult rv = helper->DispatchToTransactionPool(); michael@0: if (NS_FAILED(rv)) { michael@0: IDB_WARNING("Failed to dispatch!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return; michael@0: } michael@0: michael@0: mContinueCalled = true; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndex) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER michael@0: NS_ASSERTION(tmp->mHaveCachedKey || JSVAL_IS_VOID(tmp->mCachedKey), michael@0: "Should have a cached key"); michael@0: NS_ASSERTION(tmp->mHaveCachedPrimaryKey || michael@0: JSVAL_IS_VOID(tmp->mCachedPrimaryKey), michael@0: "Should have a cached primary key"); michael@0: NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue), michael@0: "Should have a cached value"); michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: // Don't unlink mObjectStore, mIndex, or mTransaction! michael@0: tmp->DropJSObjects(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor) michael@0: michael@0: JSObject* michael@0: IDBCursor::WrapObject(JSContext* aCx) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: switch (mType) { michael@0: case OBJECTSTORE: michael@0: case INDEXOBJECT: michael@0: return IDBCursorWithValueBinding::Wrap(aCx, this); michael@0: michael@0: case OBJECTSTOREKEY: michael@0: case INDEXKEY: michael@0: return IDBCursorBinding::Wrap(aCx, this); michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad type!"); michael@0: } michael@0: } michael@0: michael@0: mozilla::dom::IDBCursorDirection michael@0: IDBCursor::GetDirection() const michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: switch (mDirection) { michael@0: case NEXT: michael@0: return mozilla::dom::IDBCursorDirection::Next; michael@0: michael@0: case NEXT_UNIQUE: michael@0: return mozilla::dom::IDBCursorDirection::Nextunique; michael@0: michael@0: case PREV: michael@0: return mozilla::dom::IDBCursorDirection::Prev; michael@0: michael@0: case PREV_UNIQUE: michael@0: return mozilla::dom::IDBCursorDirection::Prevunique; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad direction!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: switch (mType) { michael@0: case OBJECTSTORE: michael@0: case OBJECTSTOREKEY: michael@0: MOZ_ASSERT(mObjectStore); michael@0: aSource.SetAsIDBObjectStore() = mObjectStore; michael@0: break; michael@0: michael@0: case INDEXKEY: michael@0: case INDEXOBJECT: michael@0: MOZ_ASSERT(mIndex); michael@0: aSource.SetAsIDBIndex() = mIndex; michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad type!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: IDBCursor::GetKey(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue); michael@0: michael@0: if (!mHaveValue) { michael@0: aResult.setUndefined(); michael@0: return; michael@0: } michael@0: michael@0: if (!mHaveCachedKey) { michael@0: if (!mRooted) { michael@0: mozilla::HoldJSObjects(this); michael@0: mRooted = true; michael@0: } michael@0: michael@0: aRv = mKey.ToJSVal(aCx, mCachedKey); michael@0: if (NS_WARN_IF(aRv.Failed())) { michael@0: return; michael@0: } michael@0: michael@0: mHaveCachedKey = true; michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mCachedKey); michael@0: aResult.set(mCachedKey); michael@0: } michael@0: michael@0: void michael@0: IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mHaveValue) { michael@0: aResult.setUndefined(); michael@0: return; michael@0: } michael@0: michael@0: if (!mHaveCachedPrimaryKey) { michael@0: if (!mRooted) { michael@0: mozilla::HoldJSObjects(this); michael@0: mRooted = true; michael@0: } michael@0: michael@0: const Key& key = michael@0: (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey; michael@0: MOZ_ASSERT(!key.IsUnset()); michael@0: michael@0: aRv = key.ToJSVal(aCx, mCachedPrimaryKey); michael@0: if (NS_WARN_IF(aRv.Failed())) { michael@0: return; michael@0: } michael@0: michael@0: mHaveCachedPrimaryKey = true; michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mCachedPrimaryKey); michael@0: aResult.set(mCachedPrimaryKey); michael@0: } michael@0: michael@0: void michael@0: IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); michael@0: michael@0: if (!mHaveValue) { michael@0: aResult.setUndefined(); michael@0: return; michael@0: } michael@0: michael@0: if (!mHaveCachedValue) { michael@0: if (!mRooted) { michael@0: mozilla::HoldJSObjects(this); michael@0: mRooted = true; michael@0: } michael@0: michael@0: JS::Rooted val(aCx); michael@0: if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &val)) { michael@0: aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); michael@0: return; michael@0: } michael@0: michael@0: mCloneReadInfo.mCloneBuffer.clear(); michael@0: michael@0: mCachedValue = val; michael@0: mHaveCachedValue = true; michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mCachedValue); michael@0: aResult.set(mCachedValue); michael@0: } michael@0: michael@0: void michael@0: IDBCursor::Continue(JSContext* aCx, michael@0: JS::Handle aKey, michael@0: ErrorResult &aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: Key key; michael@0: aRv = key.SetFromJSVal(aCx, aKey); michael@0: ENSURE_SUCCESS_VOID(aRv); michael@0: michael@0: if (!key.IsUnset()) { michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: if (key <= mKey) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: if (key >= mKey) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown direction type!"); michael@0: } michael@0: } michael@0: michael@0: ContinueInternal(key, 1, aRv); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef IDB_PROFILER_USE_MARKS michael@0: if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).cursor(%s)." michael@0: "continue(%s)", michael@0: "IDBRequest[%llu] MT IDBCursor.continue()", michael@0: Request()->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mDirection), michael@0: key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "cursor(%s).continue(%s)", michael@0: "IDBRequest[%llu] MT IDBCursor.continue()", michael@0: Request()->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mIndex), michael@0: IDB_PROFILER_STRING(mDirection), michael@0: key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBCursor::Update(JSContext* aCx, JS::Handle aValue, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mTransaction->IsWriteAllowed()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(mObjectStore); michael@0: MOZ_ASSERT(!mKey.IsUnset()); michael@0: MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); michael@0: MOZ_ASSERT_IF(mType == INDEXOBJECT, !mObjectKey.IsUnset()); michael@0: michael@0: const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; michael@0: michael@0: nsRefPtr request; michael@0: if (mObjectStore->HasValidKeyPath()) { michael@0: // Make sure the object given has the correct keyPath value set on it. michael@0: const KeyPath& keyPath = mObjectStore->GetKeyPath(); michael@0: Key key; michael@0: michael@0: aRv = keyPath.ExtractKey(aCx, aValue, key); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (key != objectKey) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: request = mObjectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: else { michael@0: JS::Rooted keyVal(aCx); michael@0: aRv = objectKey.ToJSVal(aCx, &keyVal); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: request = mObjectStore->Put(aCx, aValue, keyVal, aRv); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: #ifdef IDB_PROFILER_USE_MARKS michael@0: { michael@0: uint64_t requestSerial = michael@0: static_cast(request.get())->GetSerialNumber(); michael@0: if (mType == OBJECTSTORE) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "cursor(%s).update(%s)", michael@0: "IDBRequest[%llu] MT IDBCursor.update()", michael@0: requestSerial, michael@0: IDB_PROFILER_STRING(mTransaction->Database()), michael@0: IDB_PROFILER_STRING(mTransaction), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mDirection), michael@0: mObjectStore->HasValidKeyPath() ? "" : michael@0: IDB_PROFILER_STRING(objectKey)); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "index(%s).cursor(%s).update(%s)", michael@0: "IDBRequest[%llu] MT IDBCursor.update()", michael@0: requestSerial, michael@0: IDB_PROFILER_STRING(mTransaction->Database()), michael@0: IDB_PROFILER_STRING(mTransaction), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mIndex), michael@0: IDB_PROFILER_STRING(mDirection), michael@0: mObjectStore->HasValidKeyPath() ? "" : michael@0: IDB_PROFILER_STRING(objectKey)); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mTransaction->IsWriteAllowed()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(mObjectStore); michael@0: MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); michael@0: MOZ_ASSERT(!mKey.IsUnset()); michael@0: michael@0: const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; michael@0: michael@0: JS::Rooted key(aCx); michael@0: aRv = objectKey.ToJSVal(aCx, &key); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: nsRefPtr request = mObjectStore->Delete(aCx, key, aRv); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: #ifdef IDB_PROFILER_USE_MARKS michael@0: { michael@0: uint64_t requestSerial = request->GetSerialNumber(); michael@0: if (mType == OBJECTSTORE) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "cursor(%s).delete(%s)", michael@0: "IDBRequest[%llu] MT IDBCursor.delete()", michael@0: requestSerial, michael@0: IDB_PROFILER_STRING(mTransaction->Database()), michael@0: IDB_PROFILER_STRING(mTransaction), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mDirection), michael@0: mObjectStore->HasValidKeyPath() ? "" : michael@0: IDB_PROFILER_STRING(objectKey)); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "index(%s).cursor(%s).delete(%s)", michael@0: "IDBRequest[%llu] MT IDBCursor.delete()", michael@0: requestSerial, michael@0: IDB_PROFILER_STRING(mTransaction->Database()), michael@0: IDB_PROFILER_STRING(mTransaction), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mIndex), michael@0: IDB_PROFILER_STRING(mDirection), michael@0: mObjectStore->HasValidKeyPath() ? "" : michael@0: IDB_PROFILER_STRING(objectKey)); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: void michael@0: IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (aCount < 1) { michael@0: aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT); michael@0: return; michael@0: } michael@0: michael@0: Key key; michael@0: ContinueInternal(key, int32_t(aCount), aRv); michael@0: ENSURE_SUCCESS_VOID(aRv); michael@0: michael@0: #ifdef IDB_PROFILER_USE_MARKS michael@0: { michael@0: if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "cursor(%s).advance(%ld)", michael@0: "IDBRequest[%llu] MT IDBCursor.advance()", michael@0: Request()->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mDirection), aCount); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "index(%s).cursor(%s).advance(%ld)", michael@0: "IDBRequest[%llu] MT IDBCursor.advance()", michael@0: Request()->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(mObjectStore), michael@0: IDB_PROFILER_STRING(mIndex), michael@0: IDB_PROFILER_STRING(mDirection), aCount); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: CursorHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mCursor = nullptr; michael@0: AsyncConnectionHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: CursorHelper::Dispatch(nsIEventTarget* aDatabaseThread) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", "CursorHelper::Dispatch"); michael@0: michael@0: if (IndexedDatabaseManager::IsMainProcess()) { michael@0: return AsyncConnectionHelper::Dispatch(aDatabaseThread); michael@0: } michael@0: michael@0: // If we've been invalidated then there's no point sending anything to the michael@0: // parent process. michael@0: if (mCursor->Transaction()->Database()->IsInvalidated()) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: IndexedDBCursorChild* cursorActor = mCursor->GetActorChild(); michael@0: NS_ASSERTION(cursorActor, "Must have an actor here!"); michael@0: michael@0: CursorRequestParams params; michael@0: nsresult rv = PackArgumentsForParentProcess(params); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NoDispatchEventTarget target; michael@0: rv = AsyncConnectionHelper::Dispatch(&target); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: mActor = new IndexedDBCursorRequestChild(this, mCursor, params.type()); michael@0: cursorActor->SendPIndexedDBRequestConstructor(mActor, params); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: michael@0: PROFILER_LABEL("IndexedDB", "ContinueHelper::DoDatabaseWork"); michael@0: michael@0: // We need to pick a query based on whether or not the cursor's mContinueToKey michael@0: // is set. If it is unset then othing was passed to continue so we'll grab the michael@0: // next item in the database that is greater than (less than, if we're running michael@0: // a PREV cursor) the current key. If it is set then a key was passed to michael@0: // continue so we'll grab the next item in the database that is greater than michael@0: // (less than, if we're running a PREV cursor) or equal to the key that was michael@0: // specified. michael@0: michael@0: nsAutoCString query; michael@0: if (mCursor->mContinueToKey.IsUnset()) { michael@0: query.Assign(mCursor->mContinueQuery); michael@0: } michael@0: else { michael@0: query.Assign(mCursor->mContinueToQuery); michael@0: } michael@0: NS_ASSERTION(!query.IsEmpty(), "Bad query!"); michael@0: michael@0: query.AppendInt(mCount); michael@0: michael@0: nsCOMPtr stmt = mTransaction->GetCachedStatement(query); michael@0: IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: mozStorageStatementScoper scoper(stmt); michael@0: michael@0: nsresult rv = BindArgumentsToStatement(stmt); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NS_ASSERTION(mCount > 0, "Not ok!"); michael@0: michael@0: bool hasResult; michael@0: for (int32_t index = 0; index < mCount; index++) { michael@0: rv = stmt->ExecuteStep(&hasResult); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (!hasResult) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (hasResult) { michael@0: rv = GatherResultsFromStatement(stmt); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: } michael@0: else { michael@0: mKey.Unset(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: UpdateCursorState(); michael@0: michael@0: if (mKey.IsUnset()) { michael@0: aVal.setNull(); michael@0: } michael@0: else { michael@0: nsresult rv = WrapNative(aCx, mCursor, aVal); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: ContinueHelper::ReleaseMainThreadObjects() michael@0: { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: CursorHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: ContinueHelper::PackArgumentsForParentProcess(CursorRequestParams& aParams) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "ContinueHelper::PackArgumentsForParentProcess"); michael@0: michael@0: ContinueParams params; michael@0: michael@0: params.key() = mCursor->mContinueToKey; michael@0: params.count() = uint32_t(mCount); michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: ContinueHelper::SendResponseToChildProcess(nsresult aResultCode) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "ContinueHelper::SendResponseToChildProcess"); michael@0: michael@0: IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); michael@0: NS_ASSERTION(actor, "How did we get this far without an actor?"); michael@0: michael@0: InfallibleTArray blobsParent; michael@0: michael@0: if (NS_SUCCEEDED(aResultCode)) { michael@0: IDBDatabase* database = mTransaction->Database(); michael@0: NS_ASSERTION(database, "This should never be null!"); michael@0: michael@0: ContentParent* contentParent = database->GetContentParent(); michael@0: NS_ASSERTION(contentParent, "This should never be null!"); michael@0: michael@0: FileManager* fileManager = database->Manager(); michael@0: NS_ASSERTION(fileManager, "This should never be null!"); michael@0: michael@0: const nsTArray& files = mCloneReadInfo.mFiles; michael@0: michael@0: aResultCode = michael@0: IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, michael@0: blobsParent); michael@0: if (NS_FAILED(aResultCode)) { michael@0: NS_WARNING("ConvertBlobsToActors failed!"); michael@0: } michael@0: } michael@0: michael@0: ResponseValue response; michael@0: if (NS_FAILED(aResultCode)) { michael@0: response = aResultCode; michael@0: } michael@0: else { michael@0: ContinueResponse continueResponse; michael@0: continueResponse.key() = mKey; michael@0: continueResponse.objectKey() = mObjectKey; michael@0: continueResponse.cloneInfo() = mCloneReadInfo; michael@0: continueResponse.blobsParent().SwapElements(blobsParent); michael@0: response = continueResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: UpdateCursorState(); michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TContinueResponse, michael@0: "Bad response type!"); michael@0: michael@0: const ContinueResponse& response = aResponseValue.get_ContinueResponse(); michael@0: michael@0: mKey = response.key(); michael@0: mObjectKey = response.objectKey(); michael@0: michael@0: const SerializedStructuredCloneReadInfo& cloneInfo = response.cloneInfo(); michael@0: michael@0: NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || michael@0: (cloneInfo.dataLength && cloneInfo.data), michael@0: "Inconsistent clone info!"); michael@0: michael@0: if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { michael@0: IDB_WARNING("Failed to copy clone buffer!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: IDBObjectStore::ConvertActorsToBlobs(response.blobsChild(), michael@0: mCloneReadInfo.mFiles); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueObjectStoreHelper::BindArgumentsToStatement( michael@0: mozIStorageStatement* aStatement) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: MOZ_ASSERT(aStatement); michael@0: michael@0: // Bind object store id. michael@0: nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), michael@0: mCursor->mObjectStore->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); michael@0: NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); michael@0: michael@0: // Bind current key. michael@0: const Key& currentKey = mCursor->mContinueToKey.IsUnset() ? michael@0: mCursor->mKey : michael@0: mCursor->mContinueToKey; michael@0: michael@0: rv = currentKey.BindToStatement(aStatement, currentKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Bind range key if it is specified. michael@0: const Key& rangeKey = mCursor->mRangeKey; michael@0: michael@0: if (!rangeKey.IsUnset()) { michael@0: rv = rangeKey.BindToStatement(aStatement, rangeKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueObjectStoreHelper::GatherResultsFromStatement( michael@0: mozIStorageStatement* aStatement) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: MOZ_ASSERT(aStatement); michael@0: michael@0: // Figure out what kind of key we have next. michael@0: nsresult rv = mKey.SetFromStatement(aStatement, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2, michael@0: mDatabase, michael@0: mCloneReadInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueObjectStoreKeyHelper::GatherResultsFromStatement( michael@0: mozIStorageStatement* aStatement) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: MOZ_ASSERT(aStatement); michael@0: michael@0: nsresult rv = mKey.SetFromStatement(aStatement, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement) michael@0: { michael@0: // Bind index id. michael@0: nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), michael@0: mCursor->mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); michael@0: michael@0: // Bind current key. michael@0: const Key& currentKey = mCursor->mContinueToKey.IsUnset() ? michael@0: mCursor->mKey : michael@0: mCursor->mContinueToKey; michael@0: michael@0: rv = currentKey.BindToStatement(aStatement, currentKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Bind range key if it is specified. michael@0: if (!mCursor->mRangeKey.IsUnset()) { michael@0: NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); michael@0: rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Bind object key if duplicates are allowed and we're not continuing to a michael@0: // specific key. michael@0: if ((mCursor->mDirection == IDBCursor::NEXT || michael@0: mCursor->mDirection == IDBCursor::PREV) && michael@0: mCursor->mContinueToKey.IsUnset()) { michael@0: NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key"); michael@0: rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueIndexHelper::GatherResultsFromStatement( michael@0: mozIStorageStatement* aStatement) michael@0: { michael@0: nsresult rv = mKey.SetFromStatement(aStatement, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mObjectKey.SetFromStatement(aStatement, 1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ContinueIndexObjectHelper::GatherResultsFromStatement( michael@0: mozIStorageStatement* aStatement) michael@0: { michael@0: nsresult rv = mKey.SetFromStatement(aStatement, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mObjectKey.SetFromStatement(aStatement, 1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3, michael@0: mDatabase, mCloneReadInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: }