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 "IDBIndex.h" michael@0: michael@0: #include michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/ipc/Blob.h" michael@0: #include "mozilla/storage.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "xpcpublic.h" michael@0: michael@0: #include "AsyncConnectionHelper.h" michael@0: #include "DatabaseInfo.h" michael@0: #include "IDBCursor.h" michael@0: #include "IDBEvents.h" michael@0: #include "IDBKeyRange.h" michael@0: #include "IDBObjectStore.h" michael@0: #include "IDBTransaction.h" michael@0: #include "ProfilerHelpers.h" michael@0: #include "ReportInternalError.h" michael@0: michael@0: #include "ipc/IndexedDBChild.h" michael@0: #include "ipc/IndexedDBParent.h" michael@0: michael@0: #include "IndexedDatabaseInlines.h" michael@0: michael@0: USING_INDEXEDDB_NAMESPACE michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::dom::indexedDB::ipc; michael@0: using mozilla::ErrorResult; michael@0: using mozilla::Move; michael@0: michael@0: namespace { michael@0: michael@0: class IndexHelper : public AsyncConnectionHelper michael@0: { michael@0: public: michael@0: IndexHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex) michael@0: : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex), michael@0: mActor(nullptr) michael@0: { michael@0: NS_ASSERTION(aTransaction, "Null transaction!"); michael@0: NS_ASSERTION(aRequest, "Null request!"); michael@0: NS_ASSERTION(aIndex, "Null index!"); 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(IndexRequestParams& aParams) = 0; michael@0: michael@0: virtual nsresult michael@0: UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; michael@0: michael@0: protected: michael@0: nsRefPtr mIndex; michael@0: michael@0: private: michael@0: IndexedDBIndexRequestChild* mActor; michael@0: }; michael@0: michael@0: class GetKeyHelper : public IndexHelper michael@0: { michael@0: public: michael@0: GetKeyHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange) michael@0: : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange) 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) MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& 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: // In-params. michael@0: nsRefPtr mKeyRange; michael@0: michael@0: // Out-params. michael@0: Key mKey; michael@0: }; michael@0: michael@0: class GetHelper : public GetKeyHelper michael@0: { michael@0: public: michael@0: GetHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange) michael@0: : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange) michael@0: { } michael@0: michael@0: ~GetHelper() michael@0: { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); 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) MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& 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: StructuredCloneReadInfo mCloneReadInfo; michael@0: }; michael@0: michael@0: class GetAllKeysHelper : public GetKeyHelper michael@0: { michael@0: public: michael@0: GetAllKeysHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange, michael@0: const uint32_t aLimit) michael@0: : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit) 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) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& 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: const uint32_t mLimit; michael@0: nsTArray mKeys; michael@0: }; michael@0: michael@0: class GetAllHelper : public GetKeyHelper michael@0: { michael@0: public: michael@0: GetAllHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange, michael@0: const uint32_t aLimit) michael@0: : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit) michael@0: { } michael@0: michael@0: ~GetAllHelper() michael@0: { michael@0: for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); michael@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) MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& 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: const uint32_t mLimit; michael@0: nsTArray mCloneReadInfos; michael@0: }; michael@0: michael@0: class OpenKeyCursorHelper : public IndexHelper michael@0: { michael@0: public: michael@0: OpenKeyCursorHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange, michael@0: IDBCursor::Direction aDirection) michael@0: : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange), michael@0: mDirection(aDirection) michael@0: { } michael@0: michael@0: ~OpenKeyCursorHelper() michael@0: { michael@0: NS_ASSERTION(true, "bas"); 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) MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& 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 nsresult EnsureCursor(); michael@0: michael@0: // In-params. michael@0: nsRefPtr mKeyRange; michael@0: const IDBCursor::Direction mDirection; michael@0: michael@0: // Out-params. michael@0: Key mKey; michael@0: Key mObjectKey; michael@0: nsCString mContinueQuery; michael@0: nsCString mContinueToQuery; michael@0: Key mRangeKey; michael@0: michael@0: // Only used in the parent process. michael@0: nsRefPtr mCursor; michael@0: }; michael@0: michael@0: class OpenCursorHelper : public OpenKeyCursorHelper michael@0: { michael@0: public: michael@0: OpenCursorHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange, michael@0: IDBCursor::Direction aDirection) michael@0: : OpenKeyCursorHelper(aTransaction, aRequest, aIndex, aKeyRange, aDirection) michael@0: { } michael@0: michael@0: ~OpenCursorHelper() michael@0: { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: } michael@0: michael@0: virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; michael@0: michael@0: virtual ChildProcessSendResult michael@0: SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: virtual nsresult EnsureCursor(); michael@0: michael@0: StructuredCloneReadInfo mCloneReadInfo; michael@0: michael@0: // Only used in the parent process. michael@0: SerializedStructuredCloneReadInfo mSerializedCloneReadInfo; michael@0: }; michael@0: michael@0: class CountHelper : public IndexHelper michael@0: { michael@0: public: michael@0: CountHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBIndex* aIndex, michael@0: IDBKeyRange* aKeyRange) michael@0: : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange), mCount(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) MOZ_OVERRIDE; michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(IndexRequestParams& 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: private: michael@0: nsRefPtr mKeyRange; michael@0: uint64_t mCount; michael@0: }; michael@0: michael@0: inline michael@0: already_AddRefed michael@0: GenerateRequest(IDBIndex* aIndex) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: IDBTransaction* transaction = aIndex->ObjectStore()->Transaction(); michael@0: IDBDatabase* database = transaction->Database(); michael@0: return IDBRequest::Create(aIndex, database, transaction); michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBIndex::Create(IDBObjectStore* aObjectStore, michael@0: const IndexInfo* aIndexInfo, michael@0: bool aCreating) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aObjectStore, "Null pointer!"); michael@0: NS_ASSERTION(aIndexInfo, "Null pointer!"); michael@0: michael@0: nsRefPtr index = new IDBIndex(); michael@0: michael@0: index->mObjectStore = aObjectStore; michael@0: index->mId = aIndexInfo->id; michael@0: index->mName = aIndexInfo->name; michael@0: index->mKeyPath = aIndexInfo->keyPath; michael@0: index->mUnique = aIndexInfo->unique; michael@0: index->mMultiEntry = aIndexInfo->multiEntry; michael@0: michael@0: if (!IndexedDatabaseManager::IsMainProcess()) { michael@0: IndexedDBObjectStoreChild* objectStoreActor = aObjectStore->GetActorChild(); michael@0: NS_ASSERTION(objectStoreActor, "Must have an actor here!"); michael@0: michael@0: nsAutoPtr actor(new IndexedDBIndexChild(index)); michael@0: michael@0: IndexConstructorParams params; michael@0: michael@0: if (aCreating) { michael@0: CreateIndexParams createParams; michael@0: createParams.info() = *aIndexInfo; michael@0: params = createParams; michael@0: } michael@0: else { michael@0: GetIndexParams getParams; michael@0: getParams.name() = aIndexInfo->name; michael@0: params = getParams; michael@0: } michael@0: michael@0: objectStoreActor->SendPIndexedDBIndexConstructor(actor.forget(), params); michael@0: } michael@0: michael@0: return index.forget(); michael@0: } michael@0: michael@0: IDBIndex::IDBIndex() michael@0: : mId(INT64_MIN), michael@0: mKeyPath(0), michael@0: mCachedKeyPath(JSVAL_VOID), michael@0: mActorChild(nullptr), michael@0: mActorParent(nullptr), michael@0: mUnique(false), michael@0: mMultiEntry(false), michael@0: mRooted(false) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: IDBIndex::~IDBIndex() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); michael@0: michael@0: if (mRooted) { michael@0: mCachedKeyPath = JSVAL_VOID; michael@0: mozilla::DropJSObjects(this); michael@0: } michael@0: 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: michael@0: already_AddRefed michael@0: IDBIndex::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new GetHelper(transaction, request, this, aKeyRange); 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 nullptr; michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "get(%s)", michael@0: "IDBRequest[%llu] MT IDBIndex.get()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::GetKeyInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new GetKeyHelper(transaction, request, this, aKeyRange); 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 nullptr; michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "getKey(%s)", michael@0: "IDBRequest[%llu] MT IDBIndex.getKey()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::GetAllInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new GetAllHelper(transaction, request, this, aKeyRange, aLimit); 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 nullptr; michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "getAll(%s, %lu)", michael@0: "IDBRequest[%llu] MT IDBIndex.getAll()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), michael@0: IDB_PROFILER_STRING(aKeyRange), aLimit); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new GetAllKeysHelper(transaction, request, this, aKeyRange, aLimit); 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 nullptr; michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "getAllKeys(%s, %lu)", michael@0: "IDBRequest[%llu] MT IDBIndex.getAllKeys()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), michael@0: aLimit); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new CountHelper(transaction, request, this, aKeyRange); 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 nullptr; michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "count(%s)", michael@0: "IDBRequest[%llu] MT IDBIndex.count()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: IDBCursor::Direction direction = michael@0: static_cast(aDirection); michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new OpenKeyCursorHelper(transaction, request, this, aKeyRange, direction); 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 nullptr; michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "openKeyCursor(%s)", michael@0: "IDBRequest[%llu] MT IDBIndex.openKeyCursor()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), michael@0: IDB_PROFILER_STRING(direction)); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: IDBIndex::OpenCursorInternal(IDBKeyRange* aKeyRange, michael@0: size_t aDirection, michael@0: IDBRequest** _retval) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; michael@0: } michael@0: michael@0: IDBCursor::Direction direction = michael@0: static_cast(aDirection); michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsRefPtr helper = michael@0: new OpenCursorHelper(transaction, request, this, aKeyRange, direction); michael@0: michael@0: nsresult rv = helper->DispatchToTransactionPool(); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).index(%s)." michael@0: "openCursor(%s)", michael@0: "IDBRequest[%llu] MT IDBIndex.openCursor()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()-> michael@0: Database()), michael@0: IDB_PROFILER_STRING(ObjectStore()->Transaction()), michael@0: IDB_PROFILER_STRING(ObjectStore()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), michael@0: IDB_PROFILER_STRING(direction)); michael@0: michael@0: request.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: IDBIndex::OpenCursorFromChildProcess(IDBRequest* aRequest, michael@0: size_t aDirection, michael@0: const Key& aKey, michael@0: const Key& aObjectKey, michael@0: IDBCursor** _retval) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBCursor::Direction direction = michael@0: static_cast(aDirection); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction, michael@0: Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey); michael@0: IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: cursor.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: IDBIndex::OpenCursorFromChildProcess( michael@0: IDBRequest* aRequest, michael@0: size_t aDirection, michael@0: const Key& aKey, michael@0: const Key& aObjectKey, michael@0: const SerializedStructuredCloneReadInfo& aCloneInfo, michael@0: nsTArray& aBlobs, michael@0: IDBCursor** _retval) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) || michael@0: (aCloneInfo.dataLength && aCloneInfo.data), michael@0: "Inconsistent clone info!"); michael@0: michael@0: IDBCursor::Direction direction = michael@0: static_cast(aDirection); michael@0: michael@0: StructuredCloneReadInfo cloneInfo; michael@0: michael@0: if (!cloneInfo.SetFromSerialized(aCloneInfo)) { michael@0: IDB_WARNING("Failed to copy clone buffer!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: cloneInfo.mFiles.SwapElements(aBlobs); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction, michael@0: Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey, michael@0: Move(cloneInfo)); michael@0: IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!"); michael@0: michael@0: cursor.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: michael@0: // Don't unlink mObjectStore! michael@0: michael@0: tmp->mCachedKeyPath = JSVAL_VOID; michael@0: michael@0: if (tmp->mRooted) { michael@0: mozilla::DropJSObjects(tmp); michael@0: tmp->mRooted = false; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) 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(IDBIndex) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex) michael@0: michael@0: JSObject* michael@0: IDBIndex::WrapObject(JSContext* aCx) michael@0: { michael@0: return IDBIndexBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!JSVAL_IS_VOID(mCachedKeyPath)) { michael@0: JS::ExposeValueToActiveJS(mCachedKeyPath); michael@0: aResult.set(mCachedKeyPath); michael@0: return; michael@0: } michael@0: michael@0: aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); michael@0: if (NS_WARN_IF(aRv.Failed())) { michael@0: return; michael@0: } michael@0: michael@0: if (JSVAL_IS_GCTHING(mCachedKeyPath)) { michael@0: mozilla::HoldJSObjects(this); michael@0: mRooted = true; michael@0: } michael@0: michael@0: JS::ExposeValueToActiveJS(mCachedKeyPath); michael@0: aResult.set(mCachedKeyPath); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: if (!keyRange) { michael@0: // Must specify a key or keyRange for getKey(). michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return GetInternal(keyRange, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: if (!keyRange) { michael@0: // Must specify a key or keyRange for get(). michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return GetKeyInternal(keyRange, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::GetAll(JSContext* aCx, JS::Handle aKey, michael@0: const Optional& aLimit, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: uint32_t limit = UINT32_MAX; michael@0: if (aLimit.WasPassed() && aLimit.Value() > 0) { michael@0: limit = aLimit.Value(); michael@0: } michael@0: michael@0: return GetAllInternal(keyRange, limit, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::GetAllKeys(JSContext* aCx, michael@0: JS::Handle aKey, michael@0: const Optional& aLimit, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: uint32_t limit = UINT32_MAX; michael@0: if (aLimit.WasPassed() && aLimit.Value() > 0) { michael@0: limit = aLimit.Value(); michael@0: } michael@0: michael@0: return GetAllKeysInternal(keyRange, limit, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::OpenCursor(JSContext* aCx, michael@0: JS::Handle aRange, michael@0: IDBCursorDirection aDirection, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: if (!request) { michael@0: IDB_WARNING("Failed to generate request!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr helper = michael@0: new OpenCursorHelper(transaction, request, this, keyRange, direction); 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 nullptr; michael@0: } michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::OpenKeyCursor(JSContext* aCx, michael@0: JS::Handle aRange, michael@0: IDBCursorDirection aDirection, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); michael@0: michael@0: return OpenKeyCursorInternal(keyRange, direction, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBIndex::Count(JSContext* aCx, JS::Handle aKey, michael@0: ErrorResult& aRv) michael@0: { michael@0: IDBTransaction* transaction = mObjectStore->Transaction(); michael@0: if (!transaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr keyRange; michael@0: aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); michael@0: ENSURE_SUCCESS(aRv, nullptr); michael@0: michael@0: return CountInternal(keyRange, aRv); michael@0: } michael@0: michael@0: void michael@0: IndexHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mIndex = nullptr; michael@0: AsyncConnectionHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: IndexHelper::Dispatch(nsIEventTarget* aDatabaseThread) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", "IndexHelper::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 (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: IndexedDBIndexChild* indexActor = mIndex->GetActorChild(); michael@0: NS_ASSERTION(indexActor, "Must have an actor here!"); michael@0: michael@0: IndexRequestParams 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 IndexedDBIndexRequestChild(this, mIndex, params.type()); michael@0: indexActor->SendPIndexedDBRequestConstructor(mActor, params); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(mKeyRange, "Must have a key range here!"); michael@0: michael@0: PROFILER_LABEL("IndexedDB", "GetKeyHelper::DoDatabaseWork"); michael@0: michael@0: nsCString indexTable; michael@0: if (mIndex->IsUnique()) { michael@0: indexTable.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: indexTable.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: nsCString keyRangeClause; michael@0: mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); michael@0: michael@0: NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + michael@0: indexTable + michael@0: NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + michael@0: keyRangeClause + michael@0: NS_LITERAL_CSTRING(" LIMIT 1"); 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), michael@0: mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = mKeyRange->BindToStatement(stmt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult; 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: rv = mKey.SetFromStatement(stmt, 0); 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: GetKeyHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: return mKey.ToJSVal(aCx, aVal); michael@0: } michael@0: michael@0: void michael@0: GetKeyHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mKeyRange = nullptr; michael@0: IndexHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: GetKeyHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(mKeyRange, "This should never be null!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "GetKeyHelper::PackArgumentsForParentProcess"); michael@0: michael@0: GetKeyParams params; michael@0: michael@0: mKeyRange->ToSerializedKeyRange(params.keyRange()); michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: GetKeyHelper::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: "GetKeyHelper::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: ResponseValue response; michael@0: if (NS_FAILED(aResultCode)) { michael@0: response = aResultCode; michael@0: } michael@0: else { michael@0: GetKeyResponse getKeyResponse; michael@0: getKeyResponse.key() = mKey; michael@0: response = getKeyResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: GetKeyHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetKeyResponse, michael@0: "Bad response type!"); michael@0: michael@0: mKey = aResponseValue.get_GetKeyResponse().key(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(mKeyRange, "Must have a key range here!"); michael@0: michael@0: PROFILER_LABEL("IndexedDB", "GetHelper::DoDatabaseWork [IDBIndex.cpp]"); michael@0: michael@0: nsCString indexTable; michael@0: if (mIndex->IsUnique()) { michael@0: indexTable.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: indexTable.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: nsCString keyRangeClause; michael@0: mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); michael@0: michael@0: NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " michael@0: "INNER JOIN ") + indexTable + michael@0: NS_LITERAL_CSTRING(" AS index_table ON object_data.id = ") + michael@0: NS_LITERAL_CSTRING("index_table.object_data_id WHERE " michael@0: "index_id = :index_id") + michael@0: keyRangeClause + michael@0: NS_LITERAL_CSTRING(" LIMIT 1"); 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), michael@0: mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = mKeyRange->BindToStatement(stmt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult; 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: rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, michael@0: mDatabase, mCloneReadInfo); 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: GetHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal); michael@0: michael@0: mCloneReadInfo.mCloneBuffer.clear(); michael@0: michael@0: NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: GetHelper::ReleaseMainThreadObjects() michael@0: { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: GetKeyHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: GetHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(mKeyRange, "This should never be null!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "GetHelper::PackArgumentsForParentProcess " michael@0: "[IDBIndex.cpp]"); michael@0: michael@0: GetParams params; michael@0: michael@0: mKeyRange->ToSerializedKeyRange(params.keyRange()); michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: GetHelper::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: "GetHelper::SendResponseToChildProcess " michael@0: "[IDBIndex.cpp]"); 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 = mIndex->ObjectStore()->Transaction()->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("ConvertBlobActors 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: GetResponse getResponse; michael@0: getResponse.cloneInfo() = mCloneReadInfo; michael@0: getResponse.blobsParent().SwapElements(blobsParent); michael@0: response = getResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse, michael@0: "Bad response type!"); michael@0: michael@0: const GetResponse& getResponse = aResponseValue.get_GetResponse(); michael@0: const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.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(getResponse.blobsChild(), michael@0: mCloneReadInfo.mFiles); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); michael@0: michael@0: PROFILER_LABEL("IndexedDB", michael@0: "GetAllKeysHelper::DoDatabaseWork [IDBIndex.cpp]"); michael@0: michael@0: nsCString tableName; michael@0: if (mIndex->IsUnique()) { michael@0: tableName.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: tableName.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: nsCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); michael@0: } michael@0: michael@0: nsCString limitClause; michael@0: if (mLimit != UINT32_MAX) { michael@0: limitClause = NS_LITERAL_CSTRING(" LIMIT "); michael@0: limitClause.AppendInt(mLimit); michael@0: } michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + michael@0: tableName + michael@0: NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + michael@0: keyRangeClause + limitClause; 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), michael@0: mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (mKeyRange) { michael@0: rv = mKeyRange->BindToStatement(stmt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: mKeys.SetCapacity(std::min(50, mLimit)); michael@0: michael@0: bool hasResult; michael@0: while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { michael@0: if (mKeys.Capacity() == mKeys.Length()) { michael@0: mKeys.SetCapacity(mKeys.Capacity() * 2); michael@0: } michael@0: michael@0: Key* key = mKeys.AppendElement(); michael@0: NS_ASSERTION(key, "This shouldn't fail!"); michael@0: michael@0: rv = key->SetFromStatement(stmt, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllKeysHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mKeys.Length() <= mLimit); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "GetAllKeysHelper::GetSuccessResult " michael@0: "[IDBIndex.cpp]"); michael@0: michael@0: nsTArray keys; michael@0: mKeys.SwapElements(keys); michael@0: michael@0: JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); michael@0: if (!array) { michael@0: IDB_WARNING("Failed to make array!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: if (!keys.IsEmpty()) { michael@0: if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) { michael@0: IDB_WARNING("Failed to set array length!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: for (uint32_t index = 0, count = keys.Length(); index < count; index++) { michael@0: const Key& key = keys[index]; michael@0: NS_ASSERTION(!key.IsUnset(), "Bad key!"); michael@0: michael@0: JS::Rooted value(aCx); michael@0: nsresult rv = key.ToJSVal(aCx, &value); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to get jsval for key!"); michael@0: return rv; michael@0: } michael@0: michael@0: if (!JS_SetElement(aCx, array, index, value)) { michael@0: IDB_WARNING("Failed to set array element!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: } michael@0: } michael@0: michael@0: aVal.setObject(*array); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllKeysHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "GetAllKeysHelper::PackArgumentsForParentProcess " michael@0: "[IDBIndex.cpp]"); michael@0: michael@0: GetAllKeysParams params; michael@0: michael@0: if (mKeyRange) { michael@0: KeyRange keyRange; michael@0: mKeyRange->ToSerializedKeyRange(keyRange); michael@0: params.optionalKeyRange() = keyRange; michael@0: } michael@0: else { michael@0: params.optionalKeyRange() = mozilla::void_t(); michael@0: } michael@0: michael@0: params.limit() = mLimit; michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "GetAllKeysHelper::SendResponseToChildProcess " michael@0: "[IDBIndex.cpp]"); 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: ResponseValue response; michael@0: if (NS_FAILED(aResultCode)) { michael@0: response = aResultCode; michael@0: } michael@0: else { michael@0: GetAllKeysResponse getAllKeysResponse; michael@0: getAllKeysResponse.keys().AppendElements(mKeys); michael@0: response = getAllKeysResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllKeysHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); michael@0: MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse); michael@0: michael@0: mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllHelper::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", "GetAllHelper::DoDatabaseWork [IDBIndex.cpp]"); michael@0: michael@0: nsCString indexTable; michael@0: if (mIndex->IsUnique()) { michael@0: indexTable.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: indexTable.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: nsCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); michael@0: } michael@0: michael@0: nsCString limitClause; michael@0: if (mLimit != UINT32_MAX) { michael@0: limitClause = NS_LITERAL_CSTRING(" LIMIT "); michael@0: limitClause.AppendInt(mLimit); michael@0: } michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " michael@0: "INNER JOIN ") + indexTable + michael@0: NS_LITERAL_CSTRING(" AS index_table ON object_data.id = " michael@0: "index_table.object_data_id " michael@0: "WHERE index_id = :index_id") + michael@0: keyRangeClause + limitClause; 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), michael@0: mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (mKeyRange) { michael@0: rv = mKeyRange->BindToStatement(stmt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: mCloneReadInfos.SetCapacity(50); michael@0: michael@0: bool hasResult; michael@0: while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { michael@0: if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) { michael@0: mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2); michael@0: } michael@0: michael@0: StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement(); michael@0: NS_ASSERTION(readInfo, "This shouldn't fail!"); michael@0: michael@0: rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, michael@0: mDatabase, *readInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!"); michael@0: michael@0: nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal); michael@0: michael@0: NS_ASSERTION(mCloneReadInfos.IsEmpty(), michael@0: "Should have cleared in ConvertToArrayAndCleanup"); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: GetAllHelper::ReleaseMainThreadObjects() michael@0: { michael@0: for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); michael@0: } michael@0: GetKeyHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: GetAllHelper::PackArgumentsForParentProcess(IndexRequestParams& 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: "GetAllHelper::PackArgumentsForParentProcess " michael@0: "[IDBIndex.cpp]"); michael@0: michael@0: GetAllParams params; michael@0: michael@0: if (mKeyRange) { michael@0: KeyRange keyRange; michael@0: mKeyRange->ToSerializedKeyRange(keyRange); michael@0: params.optionalKeyRange() = keyRange; michael@0: } michael@0: else { michael@0: params.optionalKeyRange() = mozilla::void_t(); michael@0: } michael@0: michael@0: params.limit() = mLimit; michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: GetAllHelper::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: "GetAllHelper::SendResponseToChildProcess " michael@0: "[IDBIndex.cpp]"); 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: GetAllResponse getAllResponse; michael@0: michael@0: if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) { michael@0: IDBDatabase* database = mIndex->ObjectStore()->Transaction()->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: uint32_t length = mCloneReadInfos.Length(); michael@0: michael@0: InfallibleTArray& infos = michael@0: getAllResponse.cloneInfos(); michael@0: infos.SetCapacity(length); michael@0: michael@0: InfallibleTArray& blobArrays = getAllResponse.blobs(); michael@0: blobArrays.SetCapacity(length); michael@0: michael@0: for (uint32_t index = 0; michael@0: NS_SUCCEEDED(aResultCode) && index < length; michael@0: index++) { michael@0: const StructuredCloneReadInfo& clone = mCloneReadInfos[index]; michael@0: michael@0: // Append the structured clone data. michael@0: SerializedStructuredCloneReadInfo* info = infos.AppendElement(); michael@0: *info = clone; michael@0: michael@0: const nsTArray& files = clone.mFiles; michael@0: michael@0: // Now take care of the files. michael@0: BlobArray* blobArray = blobArrays.AppendElement(); michael@0: michael@0: InfallibleTArray& blobs = blobArray->blobsParent(); michael@0: michael@0: aResultCode = michael@0: IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, michael@0: blobs); michael@0: if (NS_FAILED(aResultCode)) { michael@0: NS_WARNING("ConvertBlobsToActors failed!"); michael@0: break; michael@0: } 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: response = getAllResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: GetAllHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse, michael@0: "Bad response type!"); michael@0: michael@0: const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse(); michael@0: const InfallibleTArray& cloneInfos = michael@0: getAllResponse.cloneInfos(); michael@0: const InfallibleTArray& blobArrays = getAllResponse.blobs(); michael@0: michael@0: mCloneReadInfos.SetCapacity(cloneInfos.Length()); michael@0: michael@0: for (uint32_t index = 0; index < cloneInfos.Length(); index++) { michael@0: const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index]; michael@0: const InfallibleTArray& blobs = blobArrays[index].blobsChild(); michael@0: michael@0: StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement(); michael@0: if (!destInfo->SetFromSerialized(srcInfo)) { 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(blobs, destInfo->mFiles); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(aConnection, "Passed a null connection!"); michael@0: michael@0: PROFILER_LABEL("IndexedDB", "OpenKeyCursorHelper::DoDatabaseWork"); michael@0: michael@0: nsCString table; michael@0: if (mIndex->IsUnique()) { michael@0: table.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: table.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(value, "value"); michael@0: michael@0: nsCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(value, keyRangeClause); michael@0: } michael@0: michael@0: nsAutoCString directionClause(" ORDER BY value "); michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: directionClause += NS_LITERAL_CSTRING("ASC, object_data_key ASC"); michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: directionClause += NS_LITERAL_CSTRING("DESC, object_data_key DESC"); michael@0: break; michael@0: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: directionClause += NS_LITERAL_CSTRING("DESC, object_data_key ASC"); michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown direction!"); michael@0: } michael@0: nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key " michael@0: "FROM ") + table + michael@0: NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + michael@0: keyRangeClause + directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT 1"); michael@0: michael@0: nsCOMPtr stmt = michael@0: mTransaction->GetCachedStatement(firstQuery); 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), michael@0: mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (mKeyRange) { michael@0: rv = mKeyRange->BindToStatement(stmt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: bool hasResult; 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: mKey.Unset(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = mKey.SetFromStatement(stmt, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mObjectKey.SetFromStatement(stmt, 1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Now we need to make the query to get the next match. michael@0: nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT value, object_data_key" michael@0: " FROM ") + table + michael@0: NS_LITERAL_CSTRING(" WHERE index_id = :id"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); michael@0: michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: if (mKeyRange && !mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Upper(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND value >= :current_key AND " michael@0: "( value > :current_key OR " michael@0: " object_data_key > :object_key )") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND value >= :current_key ") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: break; michael@0: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: if (mKeyRange && !mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Upper(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + NS_LITERAL_CSTRING(" AND value > :current_key") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: mContinueToQuery = michael@0: queryStart + NS_LITERAL_CSTRING(" AND value >= :current_key") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: if (mKeyRange && !mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Lower(); michael@0: } michael@0: michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND value <= :current_key AND " michael@0: "( value < :current_key OR " michael@0: " object_data_key < :object_key )") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND value <= :current_key ") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: break; michael@0: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: if (mKeyRange && !mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Lower(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND value < :current_key") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND value <= :current_key") + michael@0: directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown direction type!"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::EnsureCursor() michael@0: { michael@0: if (mCursor || mKey.IsUnset()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey, michael@0: mContinueQuery, mContinueToQuery, mKey, mObjectKey); michael@0: IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: mCursor.swap(cursor); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: nsresult rv = EnsureCursor(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mCursor) { michael@0: rv = WrapNative(aCx, mCursor, aVal); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: } michael@0: else { michael@0: aVal.setUndefined(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: OpenKeyCursorHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mKeyRange = nullptr; michael@0: mCursor = nullptr; michael@0: IndexHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& 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: "OpenKeyCursorHelper::" michael@0: "PackArgumentsForParentProcess [IDBIndex.cpp]"); michael@0: michael@0: OpenKeyCursorParams params; michael@0: michael@0: if (mKeyRange) { michael@0: KeyRange keyRange; michael@0: mKeyRange->ToSerializedKeyRange(keyRange); michael@0: params.optionalKeyRange() = keyRange; michael@0: } michael@0: else { michael@0: params.optionalKeyRange() = mozilla::void_t(); michael@0: } michael@0: michael@0: params.direction() = mDirection; michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "OpenKeyCursorHelper::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: if (NS_SUCCEEDED(aResultCode)) { michael@0: nsresult rv = EnsureCursor(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("EnsureCursor failed!"); michael@0: aResultCode = rv; 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: OpenCursorResponse openCursorResponse; michael@0: michael@0: if (!mCursor) { michael@0: openCursorResponse = mozilla::void_t(); michael@0: } michael@0: else { michael@0: IndexedDBIndexParent* indexActor = mIndex->GetActorParent(); michael@0: NS_ASSERTION(indexActor, "Must have an actor here!"); michael@0: michael@0: IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); michael@0: NS_ASSERTION(requestActor, "Must have an actor here!"); michael@0: michael@0: IndexCursorConstructorParams params; michael@0: params.requestParent() = requestActor; michael@0: params.direction() = mDirection; michael@0: params.key() = mKey; michael@0: params.objectKey() = mObjectKey; michael@0: params.optionalCloneInfo() = mozilla::void_t(); michael@0: michael@0: if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) { michael@0: return Error; michael@0: } michael@0: } michael@0: michael@0: response = openCursorResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse, michael@0: "Bad response type!"); michael@0: NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() == michael@0: OpenCursorResponse::Tvoid_t || michael@0: aResponseValue.get_OpenCursorResponse().type() == michael@0: OpenCursorResponse::TPIndexedDBCursorChild, michael@0: "Bad response union type!"); michael@0: NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); michael@0: michael@0: const OpenCursorResponse& response = michael@0: aResponseValue.get_OpenCursorResponse(); michael@0: michael@0: switch (response.type()) { michael@0: case OpenCursorResponse::Tvoid_t: michael@0: break; michael@0: michael@0: case OpenCursorResponse::TPIndexedDBCursorChild: { michael@0: IndexedDBCursorChild* actor = michael@0: static_cast( michael@0: response.get_PIndexedDBCursorChild()); michael@0: michael@0: mCursor = actor->ForgetStrongCursor(); michael@0: NS_ASSERTION(mCursor, "This should never be null!"); michael@0: michael@0: } break; michael@0: michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(aConnection, "Passed a null connection!"); michael@0: michael@0: PROFILER_LABEL("IndexedDB", michael@0: "OpenCursorHelper::DoDatabaseWork [IDBIndex.cpp]"); michael@0: michael@0: nsCString indexTable; michael@0: if (mIndex->IsUnique()) { michael@0: indexTable.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: indexTable.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(value, "index_table.value"); michael@0: michael@0: nsCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(value, keyRangeClause); michael@0: } michael@0: michael@0: nsAutoCString directionClause(" ORDER BY index_table.value "); michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: directionClause += michael@0: NS_LITERAL_CSTRING("ASC, index_table.object_data_key ASC"); michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: directionClause += michael@0: NS_LITERAL_CSTRING("DESC, index_table.object_data_key DESC"); michael@0: break; michael@0: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: directionClause += michael@0: NS_LITERAL_CSTRING("DESC, index_table.object_data_key ASC"); michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown direction!"); michael@0: } michael@0: michael@0: nsCString firstQuery = michael@0: NS_LITERAL_CSTRING("SELECT index_table.value, " michael@0: "index_table.object_data_key, object_data.data, " michael@0: "object_data.file_ids FROM ") + michael@0: indexTable + michael@0: NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " michael@0: "index_table.object_data_id = object_data.id " michael@0: "WHERE index_table.index_id = :id") + michael@0: keyRangeClause + directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT 1"); michael@0: michael@0: nsCOMPtr stmt = michael@0: mTransaction->GetCachedStatement(firstQuery); 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (mKeyRange) { michael@0: rv = mKeyRange->BindToStatement(stmt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: bool hasResult; 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: mKey.Unset(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = mKey.SetFromStatement(stmt, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = mObjectKey.SetFromStatement(stmt, 1); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3, michael@0: mDatabase, mCloneReadInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Now we need to make the query to get the next match. michael@0: nsAutoCString queryStart = michael@0: NS_LITERAL_CSTRING("SELECT index_table.value, " michael@0: "index_table.object_data_key, object_data.data, " michael@0: "object_data.file_ids FROM ") + michael@0: indexTable + michael@0: NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " michael@0: "index_table.object_data_id = object_data.id " michael@0: "WHERE index_table.index_id = :id"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(limit, " LIMIT "); michael@0: michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: if (mKeyRange && !mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Upper(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value >= :current_key AND " michael@0: "( index_table.value > :current_key OR " michael@0: " index_table.object_data_key > :object_key ) ") + michael@0: directionClause + limit; michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + michael@0: directionClause + limit; michael@0: break; michael@0: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: if (mKeyRange && !mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Upper(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value > :current_key") + michael@0: directionClause + limit; michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + michael@0: directionClause + limit; michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: if (mKeyRange && !mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Lower(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value <= :current_key AND " michael@0: "( index_table.value < :current_key OR " michael@0: " index_table.object_data_key < :object_key ) ") + michael@0: directionClause + limit; michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + michael@0: directionClause + limit; michael@0: break; michael@0: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: if (mKeyRange && !mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), michael@0: queryStart); michael@0: mRangeKey = mKeyRange->Lower(); michael@0: } michael@0: mContinueQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value < :current_key") + michael@0: directionClause + limit; michael@0: mContinueToQuery = michael@0: queryStart + michael@0: NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + michael@0: directionClause + limit; michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown direction type!"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: OpenCursorHelper::EnsureCursor() michael@0: { michael@0: if (mCursor || mKey.IsUnset()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mSerializedCloneReadInfo = mCloneReadInfo; michael@0: michael@0: NS_ASSERTION(mSerializedCloneReadInfo.data && michael@0: mSerializedCloneReadInfo.dataLength, michael@0: "Shouldn't be possible!"); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey, michael@0: mContinueQuery, mContinueToQuery, mKey, mObjectKey, michael@0: Move(mCloneReadInfo)); michael@0: IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!"); michael@0: michael@0: mCursor.swap(cursor); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: OpenCursorHelper::ReleaseMainThreadObjects() michael@0: { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: michael@0: // These don't need to be released on the main thread but they're only valid michael@0: // as long as mCursor is set. michael@0: mSerializedCloneReadInfo.data = nullptr; michael@0: mSerializedCloneReadInfo.dataLength = 0; michael@0: michael@0: OpenKeyCursorHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: OpenCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& 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: "OpenCursorHelper::PackArgumentsForParentProcess " michael@0: "[IDBIndex.cpp]"); michael@0: michael@0: OpenCursorParams params; michael@0: michael@0: if (mKeyRange) { michael@0: KeyRange keyRange; michael@0: mKeyRange->ToSerializedKeyRange(keyRange); michael@0: params.optionalKeyRange() = keyRange; michael@0: } michael@0: else { michael@0: params.optionalKeyRange() = mozilla::void_t(); michael@0: } michael@0: michael@0: params.direction() = mDirection; michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "OpenCursorHelper::SendResponseToChildProcess " michael@0: "[IDBIndex.cpp]"); 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 = mIndex->ObjectStore()->Transaction()->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: if (NS_SUCCEEDED(aResultCode)) { michael@0: nsresult rv = EnsureCursor(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("EnsureCursor failed!"); michael@0: aResultCode = rv; 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: OpenCursorResponse openCursorResponse; michael@0: michael@0: if (!mCursor) { michael@0: openCursorResponse = mozilla::void_t(); michael@0: } michael@0: else { michael@0: IndexedDBIndexParent* indexActor = mIndex->GetActorParent(); michael@0: NS_ASSERTION(indexActor, "Must have an actor here!"); michael@0: michael@0: IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); michael@0: NS_ASSERTION(requestActor, "Must have an actor here!"); michael@0: michael@0: NS_ASSERTION(mSerializedCloneReadInfo.data && michael@0: mSerializedCloneReadInfo.dataLength, michael@0: "Shouldn't be possible!"); michael@0: michael@0: IndexCursorConstructorParams params; michael@0: params.requestParent() = requestActor; michael@0: params.direction() = mDirection; michael@0: params.key() = mKey; michael@0: params.objectKey() = mObjectKey; michael@0: params.optionalCloneInfo() = mSerializedCloneReadInfo; michael@0: params.blobsParent().SwapElements(blobsParent); michael@0: michael@0: if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) { michael@0: return Error; michael@0: } michael@0: } michael@0: michael@0: response = openCursorResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: CountHelper::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", "CountHelper::DoDatabaseWork [IDBIndex.cpp]"); michael@0: michael@0: nsCString table; michael@0: if (mIndex->IsUnique()) { michael@0: table.AssignLiteral("unique_index_data"); michael@0: } michael@0: else { michael@0: table.AssignLiteral("index_data"); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); michael@0: NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); michael@0: NS_NAMED_LITERAL_CSTRING(value, "value"); michael@0: michael@0: nsAutoCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: if (!mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(value, lowerKeyName, false, michael@0: !mKeyRange->IsLowerOpen(), keyRangeClause); michael@0: } michael@0: if (!mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(value, upperKeyName, true, michael@0: !mKeyRange->IsUpperOpen(), keyRangeClause); michael@0: } michael@0: } michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM ") + table + michael@0: NS_LITERAL_CSTRING(" WHERE index_id = :id") + michael@0: keyRangeClause; 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 = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (mKeyRange) { michael@0: if (!mKeyRange->Lower().IsUnset()) { michael@0: rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: if (!mKeyRange->Upper().IsUnset()) { michael@0: rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: bool hasResult; michael@0: rv = stmt->ExecuteStep(&hasResult); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: mCount = stmt->AsInt64(0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CountHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: aVal.setNumber(static_cast(mCount)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: CountHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mKeyRange = nullptr; michael@0: IndexHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: CountHelper::PackArgumentsForParentProcess(IndexRequestParams& 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: "CountHelper::PackArgumentsForParentProcess " michael@0: "[IDBIndex.cpp]"); michael@0: michael@0: CountParams params; michael@0: michael@0: if (mKeyRange) { michael@0: KeyRange keyRange; michael@0: mKeyRange->ToSerializedKeyRange(keyRange); michael@0: params.optionalKeyRange() = keyRange; michael@0: } michael@0: else { michael@0: params.optionalKeyRange() = mozilla::void_t(); michael@0: } michael@0: michael@0: aParams = params; michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: CountHelper::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: "CountHelper::SendResponseToChildProcess " michael@0: "[IDBIndex.cpp]"); 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: ResponseValue response; michael@0: if (NS_FAILED(aResultCode)) { michael@0: response = aResultCode; michael@0: } michael@0: else { michael@0: CountResponse countResponse = mCount; michael@0: response = countResponse; michael@0: } michael@0: michael@0: if (!actor->SendResponse(response)) { michael@0: return Error; michael@0: } michael@0: michael@0: return Success_Sent; michael@0: } michael@0: michael@0: nsresult michael@0: CountHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse, michael@0: "Bad response type!"); michael@0: michael@0: mCount = aResponseValue.get_CountResponse().count(); michael@0: return NS_OK; michael@0: }