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 "IDBObjectStore.h" michael@0: michael@0: #include "mozilla/dom/ipc/nsIRemoteBlob.h" michael@0: #include "nsIOutputStream.h" michael@0: michael@0: #include michael@0: #include "jsfriendapi.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/FileHandleBinding.h" michael@0: #include "mozilla/dom/StructuredCloneTags.h" michael@0: #include "mozilla/dom/ipc/Blob.h" michael@0: #include "mozilla/dom/quota/FileStreams.h" michael@0: #include "mozilla/Endian.h" michael@0: #include "mozilla/storage.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsDOMClassInfo.h" michael@0: #include "nsDOMFile.h" michael@0: #include "mozilla/dom/DOMStringList.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "snappy/snappy.h" michael@0: michael@0: #include "AsyncConnectionHelper.h" michael@0: #include "IDBCursor.h" michael@0: #include "IDBEvents.h" michael@0: #include "IDBFileHandle.h" michael@0: #include "IDBIndex.h" michael@0: #include "IDBKeyRange.h" michael@0: #include "IDBTransaction.h" michael@0: #include "DatabaseInfo.h" michael@0: #include "KeyPath.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: #include "nsCharSeparatedTokenizer.h" michael@0: michael@0: #define FILE_COPY_BUFFER_SIZE 32768 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::dom::quota::FileOutputStream; michael@0: using mozilla::ErrorResult; michael@0: using mozilla::fallible_t; michael@0: using mozilla::LittleEndian; michael@0: using mozilla::Move; michael@0: using mozilla::NativeEndian; michael@0: michael@0: BEGIN_INDEXEDDB_NAMESPACE michael@0: michael@0: struct FileHandleData michael@0: { michael@0: nsString type; michael@0: nsString name; michael@0: }; michael@0: michael@0: struct BlobOrFileData michael@0: { michael@0: BlobOrFileData() michael@0: : tag(0), size(0), lastModifiedDate(UINT64_MAX) michael@0: { } michael@0: michael@0: uint32_t tag; michael@0: uint64_t size; michael@0: nsString type; michael@0: nsString name; michael@0: uint64_t lastModifiedDate; michael@0: }; michael@0: michael@0: END_INDEXEDDB_NAMESPACE michael@0: michael@0: namespace { michael@0: michael@0: inline michael@0: bool michael@0: IgnoreNothing(char16_t c) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: class ObjectStoreHelper : public AsyncConnectionHelper michael@0: { michael@0: public: michael@0: ObjectStoreHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore) michael@0: : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore), michael@0: mActor(nullptr) michael@0: { michael@0: NS_ASSERTION(aTransaction, "Null transaction!"); michael@0: NS_ASSERTION(aRequest, "Null request!"); michael@0: NS_ASSERTION(aObjectStore, "Null object store!"); 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(ObjectStoreRequestParams& aParams) = 0; michael@0: michael@0: virtual nsresult michael@0: UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; michael@0: michael@0: protected: michael@0: nsRefPtr mObjectStore; michael@0: michael@0: private: michael@0: IndexedDBObjectStoreRequestChild* mActor; michael@0: }; michael@0: michael@0: class NoRequestObjectStoreHelper : public AsyncConnectionHelper michael@0: { michael@0: public: michael@0: NoRequestObjectStoreHelper(IDBTransaction* aTransaction, michael@0: IDBObjectStore* aObjectStore) michael@0: : AsyncConnectionHelper(aTransaction, nullptr), mObjectStore(aObjectStore) michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(aTransaction, "Null transaction!"); michael@0: NS_ASSERTION(aObjectStore, "Null object store!"); michael@0: } michael@0: michael@0: virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual ChildProcessSendResult michael@0: SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult OnSuccess() MOZ_OVERRIDE; michael@0: michael@0: virtual void OnError() MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: nsRefPtr mObjectStore; michael@0: }; michael@0: michael@0: class AddHelper : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: AddHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: StructuredCloneWriteInfo&& aCloneWriteInfo, michael@0: const Key& aKey, michael@0: bool aOverwrite, michael@0: nsTArray& aIndexUpdateInfo) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: mCloneWriteInfo(Move(aCloneWriteInfo)), michael@0: mKey(aKey), michael@0: mOverwrite(aOverwrite) michael@0: { michael@0: mIndexUpdateInfo.SwapElements(aIndexUpdateInfo); michael@0: } michael@0: michael@0: ~AddHelper() michael@0: { michael@0: IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); 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(ObjectStoreRequestParams& 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: // These may change in the autoincrement case. michael@0: StructuredCloneWriteInfo mCloneWriteInfo; michael@0: Key mKey; michael@0: nsTArray mIndexUpdateInfo; michael@0: const bool mOverwrite; michael@0: }; michael@0: michael@0: class GetHelper : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: GetHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: mKeyRange(aKeyRange) michael@0: { michael@0: NS_ASSERTION(aKeyRange, "Null key range!"); 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(ObjectStoreRequestParams& 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: private: michael@0: // Out-params. michael@0: StructuredCloneReadInfo mCloneReadInfo; michael@0: }; michael@0: michael@0: class DeleteHelper : public GetHelper michael@0: { michael@0: public: michael@0: DeleteHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange) michael@0: : GetHelper(aTransaction, aRequest, aObjectStore, 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 nsresult michael@0: PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: michael@0: class ClearHelper : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: ClearHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore) michael@0: { } michael@0: michael@0: virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: michael@0: class OpenCursorHelper : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: OpenCursorHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange, michael@0: IDBCursor::Direction aDirection) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: mKeyRange(aKeyRange), mDirection(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 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(ObjectStoreRequestParams& 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: 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: StructuredCloneReadInfo mCloneReadInfo; 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: SerializedStructuredCloneReadInfo mSerializedCloneReadInfo; michael@0: }; michael@0: michael@0: class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: OpenKeyCursorHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange, michael@0: IDBCursor::Direction aDirection) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: mKeyRange(aKeyRange), mDirection(aDirection) michael@0: { } michael@0: michael@0: virtual nsresult michael@0: DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: ~OpenKeyCursorHelper() michael@0: { } michael@0: michael@0: 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: 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 CreateIndexHelper : public NoRequestObjectStoreHelper michael@0: { michael@0: public: michael@0: CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex) michael@0: : NoRequestObjectStoreHelper(aTransaction, aIndex->ObjectStore()), michael@0: mIndex(aIndex) michael@0: { michael@0: if (sTLSIndex == BAD_TLS_INDEX) { michael@0: PR_NewThreadPrivateIndex(&sTLSIndex, DestroyTLSEntry); michael@0: } michael@0: michael@0: NS_ASSERTION(sTLSIndex != BAD_TLS_INDEX, michael@0: "PR_NewThreadPrivateIndex failed!"); 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: private: michael@0: nsresult InsertDataFromObjectStore(mozIStorageConnection* aConnection); michael@0: michael@0: static void DestroyTLSEntry(void* aPtr); michael@0: michael@0: static unsigned sTLSIndex; michael@0: michael@0: // In-params. michael@0: nsRefPtr mIndex; michael@0: }; michael@0: michael@0: unsigned CreateIndexHelper::sTLSIndex = unsigned(BAD_TLS_INDEX); michael@0: michael@0: class DeleteIndexHelper : public NoRequestObjectStoreHelper michael@0: { michael@0: public: michael@0: DeleteIndexHelper(IDBTransaction* aTransaction, michael@0: IDBObjectStore* aObjectStore, michael@0: const nsAString& aName) michael@0: : NoRequestObjectStoreHelper(aTransaction, aObjectStore), mName(aName) michael@0: { } michael@0: michael@0: virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) michael@0: MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: // In-params michael@0: nsString mName; michael@0: }; michael@0: michael@0: class GetAllHelper : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: GetAllHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange, michael@0: const uint32_t aLimit) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: mKeyRange(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(ObjectStoreRequestParams& 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: const uint32_t mLimit; michael@0: michael@0: private: michael@0: // Out-params. michael@0: nsTArray mCloneReadInfos; michael@0: }; michael@0: michael@0: class GetAllKeysHelper MOZ_FINAL : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: GetAllKeysHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange, michael@0: const uint32_t aLimit) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: mKeyRange(aKeyRange), mLimit(aLimit) michael@0: { } michael@0: michael@0: virtual nsresult michael@0: DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) michael@0: MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: ReleaseMainThreadObjects() MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult michael@0: PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: ~GetAllKeysHelper() michael@0: { } michael@0: michael@0: nsRefPtr mKeyRange; michael@0: const uint32_t mLimit; michael@0: nsTArray mKeys; michael@0: }; michael@0: michael@0: class CountHelper : public ObjectStoreHelper michael@0: { michael@0: public: michael@0: CountHelper(IDBTransaction* aTransaction, michael@0: IDBRequest* aRequest, michael@0: IDBObjectStore* aObjectStore, michael@0: IDBKeyRange* aKeyRange) michael@0: : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), michael@0: 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(ObjectStoreRequestParams& 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: class MOZ_STACK_CLASS AutoRemoveIndex michael@0: { michael@0: public: michael@0: AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo, michael@0: const nsAString& aIndexName) michael@0: : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName) michael@0: { } michael@0: michael@0: ~AutoRemoveIndex() michael@0: { michael@0: if (mObjectStoreInfo) { michael@0: for (uint32_t i = 0; i < mObjectStoreInfo->indexes.Length(); i++) { michael@0: if (mObjectStoreInfo->indexes[i].name == mIndexName) { michael@0: mObjectStoreInfo->indexes.RemoveElementAt(i); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void forget() michael@0: { michael@0: mObjectStoreInfo = nullptr; michael@0: } michael@0: michael@0: private: michael@0: ObjectStoreInfo* mObjectStoreInfo; michael@0: nsString mIndexName; michael@0: }; michael@0: michael@0: class ThreadLocalJSRuntime michael@0: { michael@0: JSRuntime* mRuntime; michael@0: JSContext* mContext; michael@0: JSObject* mGlobal; michael@0: michael@0: static const JSClass sGlobalClass; michael@0: static const unsigned sRuntimeHeapSize = 768 * 1024; michael@0: michael@0: ThreadLocalJSRuntime() michael@0: : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(ThreadLocalJSRuntime); michael@0: } michael@0: michael@0: nsresult Init() michael@0: { michael@0: mRuntime = JS_NewRuntime(sRuntimeHeapSize, JS_NO_HELPER_THREADS); michael@0: NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: /* michael@0: * Not setting this will cause JS_CHECK_RECURSION to report false michael@0: * positives michael@0: */ michael@0: JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024); michael@0: michael@0: mContext = JS_NewContext(mRuntime, 0); michael@0: NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: JSAutoRequest ar(mContext); michael@0: michael@0: mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr, michael@0: JS::FireOnNewGlobalHook); michael@0: NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: js::SetDefaultObjectForContext(mContext, mGlobal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: public: michael@0: static ThreadLocalJSRuntime *Create() michael@0: { michael@0: ThreadLocalJSRuntime *entry = new ThreadLocalJSRuntime(); michael@0: NS_ENSURE_TRUE(entry, nullptr); michael@0: michael@0: if (NS_FAILED(entry->Init())) { michael@0: delete entry; michael@0: return nullptr; michael@0: } michael@0: michael@0: return entry; michael@0: } michael@0: michael@0: JSContext *Context() const michael@0: { michael@0: return mContext; michael@0: } michael@0: michael@0: JSObject *Global() const michael@0: { michael@0: return mGlobal; michael@0: } michael@0: michael@0: ~ThreadLocalJSRuntime() michael@0: { michael@0: MOZ_COUNT_DTOR(ThreadLocalJSRuntime); michael@0: michael@0: if (mContext) { michael@0: JS_DestroyContext(mContext); michael@0: } michael@0: michael@0: if (mRuntime) { michael@0: JS_DestroyRuntime(mRuntime); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: const JSClass ThreadLocalJSRuntime::sGlobalClass = { michael@0: "IndexedDBTransactionThreadGlobal", michael@0: JSCLASS_GLOBAL_FLAGS, michael@0: JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, michael@0: nullptr, nullptr, nullptr, nullptr, michael@0: JS_GlobalObjectTraceHook michael@0: }; michael@0: michael@0: inline michael@0: already_AddRefed michael@0: GenerateRequest(IDBObjectStore* aObjectStore) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: IDBDatabase* database = aObjectStore->Transaction()->Database(); michael@0: return IDBRequest::Create(aObjectStore, database, michael@0: aObjectStore->Transaction()); michael@0: } michael@0: michael@0: struct MOZ_STACK_CLASS GetAddInfoClosure michael@0: { michael@0: IDBObjectStore* mThis; michael@0: StructuredCloneWriteInfo& mCloneWriteInfo; michael@0: JS::Handle mValue; michael@0: }; michael@0: michael@0: nsresult michael@0: GetAddInfoCallback(JSContext* aCx, void* aClosure) michael@0: { michael@0: GetAddInfoClosure* data = static_cast(aClosure); michael@0: michael@0: data->mCloneWriteInfo.mOffsetToKeyProp = 0; michael@0: data->mCloneWriteInfo.mTransaction = data->mThis->Transaction(); michael@0: michael@0: if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo, data->mValue)) { michael@0: return NS_ERROR_DOM_DATA_CLONE_ERR; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: inline michael@0: BlobChild* michael@0: ActorFromRemoteBlob(nsIDOMBlob* aBlob) michael@0: { michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsCOMPtr remoteBlob = do_QueryInterface(aBlob); michael@0: if (remoteBlob) { michael@0: BlobChild* actor = michael@0: static_cast(static_cast(remoteBlob->GetPBlob())); michael@0: NS_ASSERTION(actor, "Null actor?!"); michael@0: return actor; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: inline michael@0: bool michael@0: ResolveMysteryFile(nsIDOMBlob* aBlob, const nsString& aName, michael@0: const nsString& aContentType, uint64_t aSize, michael@0: uint64_t aLastModifiedDate) michael@0: { michael@0: BlobChild* actor = ActorFromRemoteBlob(aBlob); michael@0: if (actor) { michael@0: return actor->SetMysteryBlobInfo(aName, aContentType, michael@0: aSize, aLastModifiedDate); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: inline michael@0: bool michael@0: ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType, michael@0: uint64_t aSize) michael@0: { michael@0: BlobChild* actor = ActorFromRemoteBlob(aBlob); michael@0: if (actor) { michael@0: return actor->SetMysteryBlobInfo(aContentType, aSize); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: class MainThreadDeserializationTraits michael@0: { michael@0: public: michael@0: static JSObject* CreateAndWrapFileHandle(JSContext* aCx, michael@0: IDBDatabase* aDatabase, michael@0: StructuredCloneFile& aFile, michael@0: const FileHandleData& aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr& fileInfo = aFile.mFileInfo; michael@0: michael@0: nsRefPtr fileHandle = IDBFileHandle::Create(aDatabase, michael@0: aData.name, aData.type, fileInfo.forget()); michael@0: michael@0: return fileHandle->WrapObject(aCx); michael@0: } michael@0: michael@0: static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx, michael@0: IDBDatabase* aDatabase, michael@0: StructuredCloneFile& aFile, michael@0: const BlobOrFileData& aData) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || michael@0: aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || michael@0: aData.tag == SCTAG_DOM_BLOB); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsRefPtr& fileInfo = aFile.mFileInfo; michael@0: michael@0: nsCOMPtr nativeFile; michael@0: if (!aFile.mFile) { michael@0: FileManager* fileManager = aDatabase->Manager(); michael@0: NS_ASSERTION(fileManager, "This should never be null!"); michael@0: michael@0: nsCOMPtr directory = fileManager->GetDirectory(); michael@0: if (!directory) { michael@0: NS_WARNING("Failed to get directory!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: nativeFile = fileManager->GetFileForId(directory, fileInfo->Id()); michael@0: if (!nativeFile) { michael@0: NS_WARNING("Failed to get file!"); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: if (aData.tag == SCTAG_DOM_BLOB) { michael@0: nsCOMPtr domBlob; michael@0: if (aFile.mFile) { michael@0: if (!ResolveMysteryBlob(aFile.mFile, aData.type, aData.size)) { michael@0: return nullptr; michael@0: } michael@0: domBlob = aFile.mFile; michael@0: } michael@0: else { michael@0: domBlob = new nsDOMFileFile(aData.type, aData.size, nativeFile, michael@0: fileInfo); michael@0: } michael@0: michael@0: JS::Rooted wrappedBlob(aCx); michael@0: rv = nsContentUtils::WrapNative(aCx, domBlob, &NS_GET_IID(nsIDOMBlob), michael@0: &wrappedBlob); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to wrap native!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return JSVAL_TO_OBJECT(wrappedBlob); michael@0: } michael@0: michael@0: nsCOMPtr domFile; michael@0: if (aFile.mFile) { michael@0: if (!ResolveMysteryFile(aFile.mFile, aData.name, aData.type, aData.size, michael@0: aData.lastModifiedDate)) { michael@0: return nullptr; michael@0: } michael@0: domFile = do_QueryInterface(aFile.mFile); michael@0: NS_ASSERTION(domFile, "This should never fail!"); michael@0: } michael@0: else { michael@0: domFile = new nsDOMFileFile(aData.name, aData.type, aData.size, michael@0: nativeFile, fileInfo); michael@0: } michael@0: michael@0: JS::Rooted wrappedFile(aCx); michael@0: rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), michael@0: &wrappedFile); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to wrap native!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return JSVAL_TO_OBJECT(wrappedFile); michael@0: } michael@0: }; michael@0: michael@0: michael@0: class CreateIndexDeserializationTraits michael@0: { michael@0: public: michael@0: static JSObject* CreateAndWrapFileHandle(JSContext* aCx, michael@0: IDBDatabase* aDatabase, michael@0: StructuredCloneFile& aFile, michael@0: const FileHandleData& aData) michael@0: { michael@0: // FileHandle can't be used in index creation, so just make a dummy object. michael@0: return JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()); michael@0: } michael@0: michael@0: static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx, michael@0: IDBDatabase* aDatabase, michael@0: StructuredCloneFile& aFile, michael@0: const BlobOrFileData& aData) michael@0: { michael@0: MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || michael@0: aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || michael@0: aData.tag == SCTAG_DOM_BLOB); michael@0: michael@0: // The following properties are available for use in index creation michael@0: // Blob.size michael@0: // Blob.type michael@0: // File.name michael@0: // File.lastModifiedDate michael@0: michael@0: JS::Rooted obj(aCx, michael@0: JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!obj) { michael@0: NS_WARNING("Failed to create object!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Technically these props go on the proto, but this detail won't change michael@0: // the results of index creation. michael@0: michael@0: JS::Rooted type(aCx, michael@0: JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length())); michael@0: if (!type || michael@0: !JS_DefineProperty(aCx, obj, "size", double(aData.size), 0) || michael@0: !JS_DefineProperty(aCx, obj, "type", type, 0)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aData.tag == SCTAG_DOM_BLOB) { michael@0: return obj; michael@0: } michael@0: michael@0: JS::Rooted name(aCx, michael@0: JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length())); michael@0: JS::Rooted date(aCx, michael@0: JS_NewDateObjectMsec(aCx, aData.lastModifiedDate)); michael@0: if (!name || !date || michael@0: !JS_DefineProperty(aCx, obj, "name", name, 0) || michael@0: !JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return obj; michael@0: } michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: const JSClass IDBObjectStore::sDummyPropJSClass = { michael@0: "dummy", 0, michael@0: JS_PropertyStub, JS_DeletePropertyStub, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JS_EnumerateStub, JS_ResolveStub, michael@0: JS_ConvertStub michael@0: }; michael@0: michael@0: // static michael@0: already_AddRefed michael@0: IDBObjectStore::Create(IDBTransaction* aTransaction, michael@0: ObjectStoreInfo* aStoreInfo, michael@0: const nsACString& aDatabaseId, michael@0: bool aCreating) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsRefPtr objectStore = new IDBObjectStore(); michael@0: michael@0: objectStore->mTransaction = aTransaction; michael@0: objectStore->mName = aStoreInfo->name; michael@0: objectStore->mId = aStoreInfo->id; michael@0: objectStore->mKeyPath = aStoreInfo->keyPath; michael@0: objectStore->mAutoIncrement = aStoreInfo->autoIncrement; michael@0: objectStore->mDatabaseId = aDatabaseId; michael@0: objectStore->mInfo = aStoreInfo; michael@0: michael@0: if (!IndexedDatabaseManager::IsMainProcess()) { michael@0: IndexedDBTransactionChild* transactionActor = aTransaction->GetActorChild(); michael@0: NS_ASSERTION(transactionActor, "Must have an actor here!"); michael@0: michael@0: ObjectStoreConstructorParams params; michael@0: michael@0: if (aCreating) { michael@0: CreateObjectStoreParams createParams; michael@0: createParams.info() = *aStoreInfo; michael@0: params = createParams; michael@0: } michael@0: else { michael@0: GetObjectStoreParams getParams; michael@0: getParams.name() = aStoreInfo->name; michael@0: params = getParams; michael@0: } michael@0: michael@0: IndexedDBObjectStoreChild* actor = michael@0: new IndexedDBObjectStoreChild(objectStore); michael@0: michael@0: transactionActor->SendPIndexedDBObjectStoreConstructor(actor, params); michael@0: } michael@0: michael@0: return objectStore.forget(); michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBObjectStore::AppendIndexUpdateInfo( michael@0: int64_t aIndexID, michael@0: const KeyPath& aKeyPath, michael@0: bool aUnique, michael@0: bool aMultiEntry, michael@0: JSContext* aCx, michael@0: JS::Handle aVal, michael@0: nsTArray& aUpdateInfoArray) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!aMultiEntry) { michael@0: Key key; michael@0: rv = aKeyPath.ExtractKey(aCx, aVal, key); michael@0: michael@0: // If an index's keypath doesn't match an object, we ignore that object. michael@0: if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); michael@0: updateInfo->indexId = aIndexID; michael@0: updateInfo->indexUnique = aUnique; michael@0: updateInfo->value = key; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: JS::Rooted val(aCx); michael@0: if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (JS_IsArrayObject(aCx, val)) { michael@0: JS::Rooted array(aCx, &val.toObject()); michael@0: uint32_t arrayLength; michael@0: if (!JS_GetArrayLength(aCx, array, &arrayLength)) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { michael@0: JS::Rooted arrayItem(aCx); michael@0: if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: Key value; michael@0: if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || michael@0: value.IsUnset()) { michael@0: // Not a value we can do anything with, ignore it. michael@0: continue; michael@0: } michael@0: michael@0: IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); michael@0: updateInfo->indexId = aIndexID; michael@0: updateInfo->indexUnique = aUnique; michael@0: updateInfo->value = value; michael@0: } michael@0: } michael@0: else { michael@0: Key value; michael@0: if (NS_FAILED(value.SetFromJSVal(aCx, val)) || michael@0: value.IsUnset()) { michael@0: // Not a value we can do anything with, ignore it. michael@0: return NS_OK; michael@0: } michael@0: michael@0: IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); michael@0: updateInfo->indexId = aIndexID; michael@0: updateInfo->indexUnique = aUnique; michael@0: updateInfo->value = value; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, michael@0: int64_t aObjectStoreId, michael@0: const Key& aObjectStoreKey, michael@0: bool aOverwrite, michael@0: int64_t aObjectDataId, michael@0: const nsTArray& aUpdateInfoArray) 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", "IDBObjectStore::UpdateIndexes"); michael@0: michael@0: nsresult rv; michael@0: michael@0: NS_ASSERTION(aObjectDataId != INT64_MIN, "Bad objectData id!"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); michael@0: michael@0: if (aOverwrite) { michael@0: nsCOMPtr deleteStmt = michael@0: aTransaction->GetCachedStatement( michael@0: "DELETE FROM unique_index_data " michael@0: "WHERE object_data_id = :object_data_id; " michael@0: "DELETE FROM index_data " michael@0: "WHERE object_data_id = :object_data_id"); michael@0: NS_ENSURE_TRUE(deleteStmt, NS_ERROR_FAILURE); michael@0: michael@0: mozStorageStatementScoper scoper(deleteStmt); michael@0: michael@0: rv = deleteStmt->BindInt64ByName(objectDataId, aObjectDataId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = deleteStmt->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Avoid lots of hash lookups for objectStores with lots of indexes by lazily michael@0: // holding the necessary statements on the stack outside the loop. michael@0: nsCOMPtr insertUniqueStmt; michael@0: nsCOMPtr insertStmt; michael@0: michael@0: uint32_t infoCount = aUpdateInfoArray.Length(); michael@0: for (uint32_t i = 0; i < infoCount; i++) { michael@0: const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i]; michael@0: michael@0: nsCOMPtr& stmt = michael@0: updateInfo.indexUnique ? insertUniqueStmt : insertStmt; michael@0: michael@0: if (!stmt) { michael@0: stmt = updateInfo.indexUnique ? michael@0: aTransaction->GetCachedStatement( michael@0: "INSERT INTO unique_index_data " michael@0: "(index_id, object_data_id, object_data_key, value) " michael@0: "VALUES (:index_id, :object_data_id, :object_data_key, :value)") : michael@0: aTransaction->GetCachedStatement( michael@0: "INSERT OR IGNORE INTO index_data (" michael@0: "index_id, object_data_id, object_data_key, value) " michael@0: "VALUES (:index_id, :object_data_id, :object_data_key, :value)"); michael@0: } michael@0: NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); michael@0: michael@0: mozStorageStatementScoper scoper(stmt); michael@0: michael@0: rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), michael@0: updateInfo.indexId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aObjectStoreKey.BindToStatement(stmt, michael@0: NS_LITERAL_CSTRING("object_data_key")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = stmt->Execute(); michael@0: if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) { michael@0: // If we're inserting multiple entries for the same unique index, then michael@0: // we might have failed to insert due to colliding with another entry for michael@0: // the same index in which case we should ignore it. michael@0: michael@0: for (int32_t j = (int32_t)i - 1; michael@0: j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId; michael@0: --j) { michael@0: if (updateInfo.value == aUpdateInfoArray[j].value) { michael@0: // We found a key with the same value for the same index. So we michael@0: // must have had a collision with a value we just inserted. michael@0: rv = NS_OK; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBObjectStore::GetStructuredCloneReadInfoFromStatement( michael@0: mozIStorageStatement* aStatement, michael@0: uint32_t aDataIndex, michael@0: uint32_t aFileIdsIndex, michael@0: IDBDatabase* aDatabase, michael@0: StructuredCloneReadInfo& aInfo) 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", michael@0: "IDBObjectStore::GetStructuredCloneReadInfoFromStatement"); michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: int32_t type; michael@0: NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) && michael@0: type == mozIStorageStatement::VALUE_TYPE_BLOB, michael@0: "Bad value type!"); michael@0: } michael@0: #endif michael@0: michael@0: const uint8_t* blobData; michael@0: uint32_t blobDataLength; michael@0: nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength, michael@0: &blobData); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: const char* compressed = reinterpret_cast(blobData); michael@0: size_t compressedLength = size_t(blobDataLength); michael@0: michael@0: static const fallible_t fallible = fallible_t(); michael@0: michael@0: size_t uncompressedLength; michael@0: if (!snappy::GetUncompressedLength(compressed, compressedLength, michael@0: &uncompressedLength)) { michael@0: IDB_WARNING("Snappy can't determine uncompressed length!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: nsAutoArrayPtr uncompressed(new (fallible) char[uncompressedLength]); michael@0: NS_ENSURE_TRUE(uncompressed, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: if (!snappy::RawUncompress(compressed, compressedLength, michael@0: uncompressed.get())) { michael@0: IDB_WARNING("Snappy can't determine uncompressed length!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer; michael@0: if (!buffer.copy(reinterpret_cast(uncompressed.get()), michael@0: uncompressedLength)) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: bool isNull; michael@0: rv = aStatement->GetIsNull(aFileIdsIndex, &isNull); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (!isNull) { michael@0: nsString ids; michael@0: rv = aStatement->GetString(aFileIdsIndex, ids); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsAutoTArray array; michael@0: rv = ConvertFileIdsToArray(ids, array); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: FileManager* fileManager = aDatabase->Manager(); michael@0: michael@0: for (uint32_t i = 0; i < array.Length(); i++) { michael@0: const int64_t& id = array[i]; michael@0: michael@0: nsRefPtr fileInfo = fileManager->GetFileInfo(id); michael@0: NS_ASSERTION(fileInfo, "Null file info!"); michael@0: michael@0: StructuredCloneFile* file = aInfo.mFiles.AppendElement(); michael@0: file->mFileInfo.swap(fileInfo); michael@0: } michael@0: } michael@0: michael@0: aInfo.mDatabase = aDatabase; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: IDBObjectStore::ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo) michael@0: { michael@0: // This is kind of tricky, we only want to release stuff on the main thread, michael@0: // but we can end up being called on other threads if we have already been michael@0: // cleared on the main thread. michael@0: if (!aWriteInfo.mCloneBuffer.data() && !aWriteInfo.mFiles.Length()) { michael@0: return; michael@0: } michael@0: michael@0: // If there's something to clear, we should be on the main thread. michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: ClearStructuredCloneBuffer(aWriteInfo.mCloneBuffer); michael@0: aWriteInfo.mFiles.Clear(); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo) michael@0: { michael@0: // This is kind of tricky, we only want to release stuff on the main thread, michael@0: // but we can end up being called on other threads if we have already been michael@0: // cleared on the main thread. michael@0: if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) { michael@0: return; michael@0: } michael@0: michael@0: // If there's something to clear, we should be on the main thread. michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer); michael@0: aReadInfo.mFiles.Clear(); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer) michael@0: { michael@0: if (aBuffer.data()) { michael@0: aBuffer.clear(); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: IDBObjectStore::DeserializeValue(JSContext* aCx, michael@0: StructuredCloneReadInfo& aCloneReadInfo, michael@0: JS::MutableHandle aValue) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), michael@0: "Should only be deserializing on the main thread!"); michael@0: NS_ASSERTION(aCx, "A JSContext is required!"); michael@0: michael@0: JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer; michael@0: michael@0: if (!buffer.data()) { michael@0: aValue.setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: JSAutoRequest ar(aCx); michael@0: michael@0: JSStructuredCloneCallbacks callbacks = { michael@0: IDBObjectStore::StructuredCloneReadCallback, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr michael@0: }; michael@0: michael@0: return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: IDBObjectStore::SerializeValue(JSContext* aCx, michael@0: StructuredCloneWriteInfo& aCloneWriteInfo, michael@0: JS::Handle aValue) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), michael@0: "Should only be serializing on the main thread!"); michael@0: NS_ASSERTION(aCx, "A JSContext is required!"); michael@0: michael@0: JSAutoRequest ar(aCx); michael@0: michael@0: JSStructuredCloneCallbacks callbacks = { michael@0: nullptr, michael@0: StructuredCloneWriteCallback, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr michael@0: }; michael@0: michael@0: JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer; michael@0: michael@0: return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo); michael@0: } michael@0: michael@0: static inline bool michael@0: StructuredCloneReadString(JSStructuredCloneReader* aReader, michael@0: nsCString& aString) michael@0: { michael@0: uint32_t length; michael@0: if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) { michael@0: NS_WARNING("Failed to read length!"); michael@0: return false; michael@0: } michael@0: length = NativeEndian::swapFromLittleEndian(length); michael@0: michael@0: if (!aString.SetLength(length, fallible_t())) { michael@0: NS_WARNING("Out of memory?"); michael@0: return false; michael@0: } michael@0: char* buffer = aString.BeginWriting(); michael@0: michael@0: if (!JS_ReadBytes(aReader, buffer, length)) { michael@0: NS_WARNING("Failed to read type!"); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: IDBObjectStore::ReadFileHandle(JSStructuredCloneReader* aReader, michael@0: FileHandleData* aRetval) michael@0: { michael@0: static_assert(SCTAG_DOM_FILEHANDLE == 0xFFFF8004, michael@0: "Update me!"); michael@0: MOZ_ASSERT(aReader && aRetval); michael@0: michael@0: nsCString type; michael@0: if (!StructuredCloneReadString(aReader, type)) { michael@0: return false; michael@0: } michael@0: CopyUTF8toUTF16(type, aRetval->type); michael@0: michael@0: nsCString name; michael@0: if (!StructuredCloneReadString(aReader, name)) { michael@0: return false; michael@0: } michael@0: CopyUTF8toUTF16(name, aRetval->name); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: IDBObjectStore::ReadBlobOrFile(JSStructuredCloneReader* aReader, michael@0: uint32_t aTag, michael@0: BlobOrFileData* aRetval) michael@0: { michael@0: static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 && michael@0: SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 && michael@0: SCTAG_DOM_FILE == 0xFFFF8005, michael@0: "Update me!"); michael@0: MOZ_ASSERT(aReader && aRetval); michael@0: MOZ_ASSERT(aTag == SCTAG_DOM_FILE || michael@0: aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || michael@0: aTag == SCTAG_DOM_BLOB); michael@0: michael@0: aRetval->tag = aTag; michael@0: michael@0: // If it's not a FileHandle, it's a Blob or a File. michael@0: uint64_t size; michael@0: if (!JS_ReadBytes(aReader, &size, sizeof(uint64_t))) { michael@0: NS_WARNING("Failed to read size!"); michael@0: return false; michael@0: } michael@0: aRetval->size = NativeEndian::swapFromLittleEndian(size); michael@0: michael@0: nsCString type; michael@0: if (!StructuredCloneReadString(aReader, type)) { michael@0: return false; michael@0: } michael@0: CopyUTF8toUTF16(type, aRetval->type); michael@0: michael@0: // Blobs are done. michael@0: if (aTag == SCTAG_DOM_BLOB) { michael@0: return true; michael@0: } michael@0: michael@0: NS_ASSERTION(aTag == SCTAG_DOM_FILE || michael@0: aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE, "Huh?!"); michael@0: michael@0: uint64_t lastModifiedDate; michael@0: if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) { michael@0: lastModifiedDate = UINT64_MAX; michael@0: } michael@0: else { michael@0: if(!JS_ReadBytes(aReader, &lastModifiedDate, sizeof(lastModifiedDate))) { michael@0: NS_WARNING("Failed to read lastModifiedDate"); michael@0: return false; michael@0: } michael@0: lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate); michael@0: } michael@0: aRetval->lastModifiedDate = lastModifiedDate; michael@0: michael@0: nsCString name; michael@0: if (!StructuredCloneReadString(aReader, name)) { michael@0: return false; michael@0: } michael@0: CopyUTF8toUTF16(name, aRetval->name); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: template michael@0: JSObject* michael@0: IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx, michael@0: JSStructuredCloneReader* aReader, michael@0: uint32_t aTag, michael@0: uint32_t aData, michael@0: void* aClosure) michael@0: { michael@0: // We need to statically assert that our tag values are what we expect michael@0: // so that if people accidentally change them they notice. michael@0: static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 && michael@0: SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 && michael@0: SCTAG_DOM_FILEHANDLE == 0xFFFF8004 && michael@0: SCTAG_DOM_FILE == 0xFFFF8005, michael@0: "You changed our structured clone tag values and just ate " michael@0: "everyone's IndexedDB data. I hope you are happy."); michael@0: michael@0: if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || michael@0: aTag == SCTAG_DOM_FILEHANDLE || michael@0: aTag == SCTAG_DOM_BLOB || michael@0: aTag == SCTAG_DOM_FILE) { michael@0: StructuredCloneReadInfo* cloneReadInfo = michael@0: reinterpret_cast(aClosure); michael@0: michael@0: if (aData >= cloneReadInfo->mFiles.Length()) { michael@0: NS_ERROR("Bad blob index!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: StructuredCloneFile& file = cloneReadInfo->mFiles[aData]; michael@0: IDBDatabase* database = cloneReadInfo->mDatabase; michael@0: michael@0: if (aTag == SCTAG_DOM_FILEHANDLE) { michael@0: FileHandleData data; michael@0: if (!ReadFileHandle(aReader, &data)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return DeserializationTraits::CreateAndWrapFileHandle(aCx, database, michael@0: file, data); michael@0: } michael@0: michael@0: BlobOrFileData data; michael@0: if (!ReadBlobOrFile(aReader, aTag, &data)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return DeserializationTraits::CreateAndWrapBlobOrFile(aCx, database, michael@0: file, data); michael@0: } michael@0: michael@0: const JSStructuredCloneCallbacks* runtimeCallbacks = michael@0: js::GetContextStructuredCloneCallbacks(aCx); michael@0: michael@0: if (runtimeCallbacks) { michael@0: return runtimeCallbacks->read(aCx, aReader, aTag, aData, nullptr); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx, michael@0: JSStructuredCloneWriter* aWriter, michael@0: JS::Handle aObj, michael@0: void* aClosure) michael@0: { michael@0: StructuredCloneWriteInfo* cloneWriteInfo = michael@0: reinterpret_cast(aClosure); michael@0: michael@0: if (JS_GetClass(aObj) == &sDummyPropJSClass) { michael@0: NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0, michael@0: "We should not have been here before!"); michael@0: cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter); michael@0: michael@0: uint64_t value = 0; michael@0: // Omit endian swap michael@0: return JS_WriteBytes(aWriter, &value, sizeof(value)); michael@0: } michael@0: michael@0: IDBTransaction* transaction = cloneWriteInfo->mTransaction; michael@0: FileManager* fileManager = transaction->Database()->Manager(); michael@0: michael@0: file::FileHandle* fileHandle = nullptr; michael@0: if (NS_SUCCEEDED(UNWRAP_OBJECT(FileHandle, aObj, fileHandle))) { michael@0: nsRefPtr fileInfo = fileHandle->GetFileInfo(); michael@0: michael@0: // Throw when trying to store non IDB file handles or IDB file handles michael@0: // across databases. michael@0: if (!fileInfo || fileInfo->Manager() != fileManager) { michael@0: return false; michael@0: } michael@0: michael@0: NS_ConvertUTF16toUTF8 convType(fileHandle->Type()); michael@0: uint32_t convTypeLength = michael@0: NativeEndian::swapToLittleEndian(convType.Length()); michael@0: michael@0: NS_ConvertUTF16toUTF8 convName(fileHandle->Name()); michael@0: uint32_t convNameLength = michael@0: NativeEndian::swapToLittleEndian(convName.Length()); michael@0: michael@0: if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILEHANDLE, michael@0: cloneWriteInfo->mFiles.Length()) || michael@0: !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) || michael@0: !JS_WriteBytes(aWriter, convType.get(), convType.Length()) || michael@0: !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) || michael@0: !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { michael@0: return false; michael@0: } michael@0: michael@0: StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement(); michael@0: file->mFileInfo = fileInfo.forget(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr wrappedNative; michael@0: nsContentUtils::XPConnect()-> michael@0: GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); michael@0: michael@0: if (wrappedNative) { michael@0: nsISupports* supports = wrappedNative->Native(); michael@0: michael@0: nsCOMPtr blob = do_QueryInterface(supports); michael@0: if (blob) { michael@0: nsCOMPtr inputStream; michael@0: michael@0: // Check if it is a blob created from this db or the blob was already michael@0: // stored in this db michael@0: nsRefPtr fileInfo = transaction->GetFileInfo(blob); michael@0: if (!fileInfo && fileManager) { michael@0: fileInfo = blob->GetFileInfo(fileManager); michael@0: michael@0: if (!fileInfo) { michael@0: fileInfo = fileManager->GetNewFileInfo(); michael@0: if (!fileInfo) { michael@0: NS_WARNING("Failed to get new file info!"); michael@0: return false; michael@0: } michael@0: michael@0: if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) { michael@0: NS_WARNING("Failed to get internal steam!"); michael@0: return false; michael@0: } michael@0: michael@0: transaction->AddFileInfo(blob, fileInfo); michael@0: } michael@0: } michael@0: michael@0: uint64_t size; michael@0: if (NS_FAILED(blob->GetSize(&size))) { michael@0: NS_WARNING("Failed to get size!"); michael@0: return false; michael@0: } michael@0: size = NativeEndian::swapToLittleEndian(size); michael@0: michael@0: nsString type; michael@0: if (NS_FAILED(blob->GetType(type))) { michael@0: NS_WARNING("Failed to get type!"); michael@0: return false; michael@0: } michael@0: NS_ConvertUTF16toUTF8 convType(type); michael@0: uint32_t convTypeLength = michael@0: NativeEndian::swapToLittleEndian(convType.Length()); michael@0: michael@0: nsCOMPtr file = do_QueryInterface(blob); michael@0: michael@0: if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, michael@0: cloneWriteInfo->mFiles.Length()) || michael@0: !JS_WriteBytes(aWriter, &size, sizeof(size)) || michael@0: !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) || michael@0: !JS_WriteBytes(aWriter, convType.get(), convType.Length())) { michael@0: return false; michael@0: } michael@0: michael@0: if (file) { michael@0: uint64_t lastModifiedDate = 0; michael@0: if (NS_FAILED(file->GetMozLastModifiedDate(&lastModifiedDate))) { michael@0: NS_WARNING("Failed to get last modified date!"); michael@0: return false; michael@0: } michael@0: michael@0: lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate); michael@0: michael@0: nsString name; michael@0: if (NS_FAILED(file->GetName(name))) { michael@0: NS_WARNING("Failed to get name!"); michael@0: return false; michael@0: } michael@0: NS_ConvertUTF16toUTF8 convName(name); michael@0: uint32_t convNameLength = michael@0: NativeEndian::swapToLittleEndian(convName.Length()); michael@0: michael@0: if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) || michael@0: !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) || michael@0: !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement(); michael@0: cloneFile->mFile = blob.forget(); michael@0: cloneFile->mFileInfo = fileInfo.forget(); michael@0: cloneFile->mInputStream = inputStream.forget(); michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // try using the runtime callbacks michael@0: const JSStructuredCloneCallbacks* runtimeCallbacks = michael@0: js::GetContextStructuredCloneCallbacks(aCx); michael@0: if (runtimeCallbacks) { michael@0: return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds, michael@0: nsTArray& aResult) michael@0: { michael@0: nsCharSeparatedTokenizerTemplate tokenizer(aFileIds, ' '); michael@0: michael@0: while (tokenizer.hasMoreTokens()) { michael@0: nsString token(tokenizer.nextToken()); michael@0: michael@0: NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!"); michael@0: michael@0: nsresult rv; michael@0: int32_t id = token.ToInteger(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int64_t* element = aResult.AppendElement(); michael@0: *element = id; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: IDBObjectStore::ConvertActorsToBlobs( michael@0: const InfallibleTArray& aActors, michael@0: nsTArray& aFiles) michael@0: { michael@0: NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!"); michael@0: michael@0: if (!aActors.IsEmpty()) { michael@0: NS_ASSERTION(ContentChild::GetSingleton(), "This should never be null!"); michael@0: michael@0: uint32_t length = aActors.Length(); michael@0: aFiles.SetCapacity(length); michael@0: michael@0: for (uint32_t index = 0; index < length; index++) { michael@0: BlobChild* actor = static_cast(aActors[index]); michael@0: michael@0: StructuredCloneFile* file = aFiles.AppendElement(); michael@0: file->mFile = actor->GetBlob(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: IDBObjectStore::ConvertBlobsToActors( michael@0: ContentParent* aContentParent, michael@0: FileManager* aFileManager, michael@0: const nsTArray& aFiles, michael@0: InfallibleTArray& aActors) michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aContentParent, "Null contentParent!"); michael@0: NS_ASSERTION(aFileManager, "Null file manager!"); michael@0: michael@0: if (!aFiles.IsEmpty()) { michael@0: nsCOMPtr directory = aFileManager->GetDirectory(); michael@0: if (!directory) { michael@0: IDB_WARNING("Failed to get directory!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: uint32_t fileCount = aFiles.Length(); michael@0: aActors.SetCapacity(fileCount); michael@0: michael@0: for (uint32_t index = 0; index < fileCount; index++) { michael@0: const StructuredCloneFile& file = aFiles[index]; michael@0: NS_ASSERTION(file.mFileInfo, "This should never be null!"); michael@0: michael@0: nsCOMPtr nativeFile = michael@0: aFileManager->GetFileForId(directory, file.mFileInfo->Id()); michael@0: if (!nativeFile) { michael@0: IDB_WARNING("Failed to get file!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: nsCOMPtr blob = new nsDOMFileFile(nativeFile, file.mFileInfo); michael@0: michael@0: BlobParent* actor = michael@0: aContentParent->GetOrCreateActorForBlob(blob); michael@0: if (!actor) { michael@0: // This can only fail if the child has crashed. michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: aActors.AppendElement(actor); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: IDBObjectStore::IDBObjectStore() michael@0: : mId(INT64_MIN), michael@0: mKeyPath(0), michael@0: mCachedKeyPath(JSVAL_VOID), michael@0: mRooted(false), michael@0: mAutoIncrement(false), michael@0: mActorChild(nullptr), michael@0: mActorParent(nullptr) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: IDBObjectStore::~IDBObjectStore() 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: 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: if (mRooted) { michael@0: mCachedKeyPath = JSVAL_VOID; michael@0: mozilla::DropJSObjects(this); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: IDBObjectStore::GetAddInfo(JSContext* aCx, michael@0: JS::Handle aValue, michael@0: JS::Handle aKeyVal, michael@0: StructuredCloneWriteInfo& aCloneWriteInfo, michael@0: Key& aKey, michael@0: nsTArray& aUpdateInfoArray) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Return DATA_ERR if a key was passed in and this objectStore uses inline michael@0: // keys. michael@0: if (!JSVAL_IS_VOID(aKeyVal) && HasValidKeyPath()) { michael@0: return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; michael@0: } michael@0: michael@0: JSAutoRequest ar(aCx); michael@0: michael@0: if (!HasValidKeyPath()) { michael@0: // Out-of-line keys must be passed in. michael@0: rv = aKey.SetFromJSVal(aCx, aKeyVal); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: else if (!mAutoIncrement) { michael@0: rv = GetKeyPath().ExtractKey(aCx, aValue, aKey); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: // Return DATA_ERR if no key was specified this isn't an autoIncrement michael@0: // objectStore. michael@0: if (aKey.IsUnset() && !mAutoIncrement) { michael@0: return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; michael@0: } michael@0: michael@0: // Figure out indexes and the index values to update here. michael@0: uint32_t count = mInfo->indexes.Length(); michael@0: aUpdateInfoArray.SetCapacity(count); // Pretty good estimate michael@0: for (uint32_t indexesIndex = 0; indexesIndex < count; indexesIndex++) { michael@0: const IndexInfo& indexInfo = mInfo->indexes[indexesIndex]; michael@0: michael@0: rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath, michael@0: indexInfo.unique, indexInfo.multiEntry, aCx, michael@0: aValue, aUpdateInfoArray); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: GetAddInfoClosure data = {this, aCloneWriteInfo, aValue}; michael@0: michael@0: if (mAutoIncrement && HasValidKeyPath()) { michael@0: NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!"); michael@0: michael@0: rv = GetKeyPath().ExtractOrCreateKey(aCx, aValue, aKey, michael@0: &GetAddInfoCallback, &data); michael@0: } michael@0: else { michael@0: rv = GetAddInfoCallback(aCx, &data); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::AddOrPut(JSContext* aCx, JS::Handle aValue, michael@0: JS::Handle aKey, michael@0: bool aOverwrite, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!IsWriteAllowed()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: StructuredCloneWriteInfo cloneWriteInfo; michael@0: Key key; michael@0: nsTArray updateInfo; michael@0: michael@0: JS::Rooted value(aCx, aValue); michael@0: aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo); michael@0: if (aRv.Failed()) { 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 AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key, michael@0: aOverwrite, updateInfo); 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: #ifdef IDB_PROFILER_USE_MARKS michael@0: if (aOverwrite) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).%s(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.put()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this), michael@0: key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).add(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.add()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this), michael@0: key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); michael@0: } michael@0: #endif michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: IDBObjectStore::AddOrPutInternal( michael@0: const SerializedStructuredCloneWriteInfo& aCloneWriteInfo, michael@0: const Key& aKey, michael@0: const InfallibleTArray& aUpdateInfoArray, michael@0: const nsTArray >& aBlobs, michael@0: bool aOverwrite, michael@0: IDBRequest** _retval) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; michael@0: } michael@0: michael@0: if (!IsWriteAllowed()) { michael@0: return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR; michael@0: } michael@0: michael@0: nsRefPtr request = GenerateRequest(this); michael@0: IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: StructuredCloneWriteInfo cloneWriteInfo; michael@0: if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) { michael@0: IDB_WARNING("Failed to copy structured clone buffer!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: if (!aBlobs.IsEmpty()) { michael@0: FileManager* fileManager = Transaction()->Database()->Manager(); michael@0: NS_ASSERTION(fileManager, "Null file manager?!"); michael@0: michael@0: uint32_t length = aBlobs.Length(); michael@0: cloneWriteInfo.mFiles.SetCapacity(length); michael@0: michael@0: for (uint32_t index = 0; index < length; index++) { michael@0: const nsCOMPtr& blob = aBlobs[index]; michael@0: michael@0: nsCOMPtr inputStream; michael@0: michael@0: nsRefPtr fileInfo = Transaction()->GetFileInfo(blob); michael@0: if (!fileInfo) { michael@0: fileInfo = blob->GetFileInfo(fileManager); michael@0: michael@0: if (!fileInfo) { michael@0: fileInfo = fileManager->GetNewFileInfo(); michael@0: if (!fileInfo) { michael@0: IDB_WARNING("Failed to get new file info!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) { michael@0: IDB_WARNING("Failed to get internal steam!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: // XXXbent This is where we should send a message back to the child to michael@0: // update the file id. michael@0: michael@0: Transaction()->AddFileInfo(blob, fileInfo); michael@0: } michael@0: } michael@0: michael@0: StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement(); michael@0: file->mFile = blob; michael@0: file->mFileInfo.swap(fileInfo); michael@0: file->mInputStream.swap(inputStream); michael@0: } michael@0: } michael@0: michael@0: Key key(aKey); michael@0: michael@0: nsTArray updateInfo(aUpdateInfoArray); michael@0: michael@0: nsRefPtr helper = michael@0: new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key, michael@0: aOverwrite, updateInfo); michael@0: michael@0: nsresult rv = helper->DispatchToTransactionPool(); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: #ifdef IDB_PROFILER_USE_MARKS michael@0: if (aOverwrite) { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).%s(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.put()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this), michael@0: key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); michael@0: } michael@0: else { michael@0: IDB_PROFILER_MARK("IndexedDB Request %llu: " michael@0: "database(%s).transaction(%s).objectStore(%s).add(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.add()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this), michael@0: key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); michael@0: } michael@0: #endif michael@0: michael@0: request.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aKeyRange, "Null pointer!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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(mTransaction, 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).get(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.get()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: IDBObjectStore::GetAllInternal(IDBKeyRange* aKeyRange, michael@0: uint32_t aLimit, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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(mTransaction, 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)." michael@0: "getAll(%s, %lu)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.getAll()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: IDBObjectStore::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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(mTransaction, 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)." michael@0: "getAllKeys(%s, %lu)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: IDBObjectStore::DeleteInternal(IDBKeyRange* aKeyRange, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(aKeyRange, "Null key range!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!IsWriteAllowed()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_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 DeleteHelper(mTransaction, 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).delete(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.delete()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: IDBObjectStore::Clear(ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!IsWriteAllowed()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_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(new ClearHelper(mTransaction, request, this)); 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).clear()", michael@0: "IDBRequest[%llu] MT IDBObjectStore.clear()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this)); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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(mTransaction, request, this, aKeyRange); 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).count(%s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.count()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: IDBObjectStore::OpenCursorInternal(IDBKeyRange* aKeyRange, michael@0: size_t aDirection, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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 OpenCursorHelper(mTransaction, 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)." michael@0: "openCursor(%s, %s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.openCursor()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: IDBObjectStore::OpenCursorFromChildProcess( michael@0: IDBRequest* aRequest, michael@0: size_t aDirection, michael@0: const Key& aKey, 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, mTransaction, this, direction, Key(), michael@0: EmptyCString(), EmptyCString(), aKey, 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: nsresult michael@0: IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest, michael@0: size_t aDirection, michael@0: const Key& aKey, michael@0: IDBCursor** _retval) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aRequest); michael@0: michael@0: auto direction = static_cast(aDirection); michael@0: michael@0: nsRefPtr cursor = michael@0: IDBCursor::Create(aRequest, mTransaction, this, direction, Key(), michael@0: EmptyCString(), EmptyCString(), aKey); 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: already_AddRefed michael@0: IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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: auto direction = static_cast(aDirection); michael@0: michael@0: nsRefPtr helper = michael@0: new OpenKeyCursorHelper(mTransaction, 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)." michael@0: "openKeyCursor(%s, %s)", michael@0: "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", michael@0: request->GetSerialNumber(), michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), 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: void michael@0: IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); michael@0: NS_ASSERTION(aInfo != mInfo, "This is nonsense"); michael@0: michael@0: mInfo = aInfo; michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::CreateIndexInternal(const IndexInfo& aInfo, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IndexInfo* indexInfo = mInfo->indexes.AppendElement(); michael@0: michael@0: indexInfo->name = aInfo.name; michael@0: indexInfo->id = aInfo.id; michael@0: indexInfo->keyPath = aInfo.keyPath; michael@0: indexInfo->unique = aInfo.unique; michael@0: indexInfo->multiEntry = aInfo.multiEntry; michael@0: michael@0: // Don't leave this in the list if we fail below! michael@0: AutoRemoveIndex autoRemove(mInfo, aInfo.name); michael@0: michael@0: nsRefPtr index = IDBIndex::Create(this, indexInfo, true); michael@0: michael@0: mCreatedIndexes.AppendElement(index); michael@0: michael@0: if (IndexedDatabaseManager::IsMainProcess()) { michael@0: nsRefPtr helper = michael@0: new CreateIndexHelper(mTransaction, index); 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: michael@0: autoRemove.forget(); michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Pseudo-request: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "createIndex(%s)", michael@0: "MT IDBObjectStore.createIndex()", michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index)); michael@0: michael@0: return index.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (mTransaction->IsFinished()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: IndexInfo* indexInfo = nullptr; michael@0: uint32_t indexCount = mInfo->indexes.Length(); michael@0: for (uint32_t index = 0; index < indexCount; index++) { michael@0: if (mInfo->indexes[index].name == aName) { michael@0: indexInfo = &(mInfo->indexes[index]); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!indexInfo) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr retval; michael@0: for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) { michael@0: nsRefPtr& index = mCreatedIndexes[i]; michael@0: if (index->Name() == aName) { michael@0: retval = index; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!retval) { michael@0: retval = IDBIndex::Create(this, indexInfo, false); michael@0: if (!retval) { michael@0: IDB_WARNING("Failed to create index!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mCreatedIndexes.AppendElement(retval)) { michael@0: IDB_WARNING("Out of memory!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: return retval.forget(); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore) 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(IDBObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) michael@0: michael@0: for (uint32_t i = 0; i < tmp->mCreatedIndexes.Length(); i++) { michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]"); michael@0: cb.NoteXPCOMChild(static_cast(tmp->mCreatedIndexes[i].get())); michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: michael@0: // Don't unlink mTransaction! michael@0: michael@0: tmp->mCreatedIndexes.Clear(); 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(IDBObjectStore) 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(IDBObjectStore) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore) michael@0: michael@0: JSObject* michael@0: IDBObjectStore::WrapObject(JSContext* aCx) michael@0: { michael@0: return IDBObjectStoreBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: IDBObjectStore::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: IDBObjectStore::GetIndexNames(ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsRefPtr list(new DOMStringList()); michael@0: michael@0: nsTArray& names = list->StringArray(); michael@0: uint32_t count = mInfo->indexes.Length(); michael@0: names.SetCapacity(count); michael@0: michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: names.InsertElementSorted(mInfo->indexes[index].name); michael@0: } michael@0: michael@0: return list.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::Get(JSContext* aCx, JS::Handle aKey, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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 GetInternal(keyRange, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::GetAll(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: if (!mTransaction->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: IDBObjectStore::Delete(JSContext* aCx, JS::Handle aKey, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!IsWriteAllowed()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_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 delete(). michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return DeleteInternal(keyRange, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::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: if (!mTransaction->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: size_t argDirection = static_cast(direction); michael@0: michael@0: return OpenCursorInternal(keyRange, argDirection, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, michael@0: const nsAString& aKeyPath, michael@0: const IDBIndexParameters& aOptionalParameters, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: KeyPath keyPath(0); michael@0: if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) || michael@0: !keyPath.IsValid()) { michael@0: aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, michael@0: const Sequence& aKeyPath, michael@0: const IDBIndexParameters& aOptionalParameters, michael@0: ErrorResult& aRv) michael@0: { michael@0: NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (!aKeyPath.Length()) { michael@0: aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: KeyPath keyPath(0); michael@0: if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath))) { michael@0: aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, michael@0: KeyPath& aKeyPath, michael@0: const IDBIndexParameters& aOptionalParameters, michael@0: ErrorResult& aRv) michael@0: { michael@0: // Check name and current mode michael@0: IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); michael@0: michael@0: if (!transaction || michael@0: transaction != mTransaction || michael@0: mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool found = false; michael@0: uint32_t indexCount = mInfo->indexes.Length(); michael@0: for (uint32_t index = 0; index < indexCount; index++) { michael@0: if (mInfo->indexes[index].name == aName) { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (found) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); michael@0: michael@0: #ifdef DEBUG michael@0: for (uint32_t index = 0; index < mCreatedIndexes.Length(); index++) { michael@0: if (mCreatedIndexes[index]->Name() == aName) { michael@0: NS_ERROR("Already created this one!"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (aOptionalParameters.mMultiEntry && aKeyPath.IsArray()) { michael@0: aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: DatabaseInfo* databaseInfo = mTransaction->DBInfo(); michael@0: michael@0: IndexInfo info; michael@0: michael@0: info.name = aName; michael@0: info.id = databaseInfo->nextIndexId++; michael@0: info.keyPath = aKeyPath; michael@0: info.unique = aOptionalParameters.mUnique; michael@0: info.multiEntry = aOptionalParameters.mMultiEntry; michael@0: michael@0: return CreateIndexInternal(info, aRv); michael@0: } michael@0: michael@0: void michael@0: IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); michael@0: michael@0: if (!transaction || michael@0: transaction != mTransaction || michael@0: mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); michael@0: michael@0: uint32_t index = 0; michael@0: for (; index < mInfo->indexes.Length(); index++) { michael@0: if (mInfo->indexes[index].name == aName) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (index == mInfo->indexes.Length()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); michael@0: return; michael@0: } michael@0: michael@0: if (IndexedDatabaseManager::IsMainProcess()) { michael@0: nsRefPtr helper = michael@0: new DeleteIndexHelper(mTransaction, this, aName); michael@0: michael@0: nsresult rv = helper->DispatchToTransactionPool(); michael@0: if (NS_FAILED(rv)) { michael@0: IDB_WARNING("Failed to dispatch!"); michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: return; michael@0: } michael@0: } michael@0: else { michael@0: NS_ASSERTION(mActorChild, "Must have an actor here!"); michael@0: michael@0: mActorChild->SendDeleteIndex(nsString(aName)); michael@0: } michael@0: michael@0: mInfo->indexes.RemoveElementAt(index); michael@0: michael@0: for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) { michael@0: if (mCreatedIndexes[i]->Name() == aName) { michael@0: mCreatedIndexes.RemoveElementAt(i); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: IDB_PROFILER_MARK("IndexedDB Pseudo-request: " michael@0: "database(%s).transaction(%s).objectStore(%s)." michael@0: "deleteIndex(\"%s\")", michael@0: "MT IDBObjectStore.deleteIndex()", michael@0: IDB_PROFILER_STRING(Transaction()->Database()), michael@0: IDB_PROFILER_STRING(Transaction()), michael@0: IDB_PROFILER_STRING(this), michael@0: NS_ConvertUTF16toUTF8(aName).get()); michael@0: } michael@0: michael@0: already_AddRefed michael@0: IDBObjectStore::Count(JSContext* aCx, michael@0: JS::Handle aKey, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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: already_AddRefed michael@0: IDBObjectStore::GetAllKeys(JSContext* aCx, michael@0: JS::Handle aKey, michael@0: const Optional& aLimit, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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: IDBObjectStore::OpenKeyCursor(JSContext* aCx, michael@0: JS::Handle aRange, michael@0: IDBCursorDirection aDirection, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mTransaction->IsOpen()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: 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, static_cast(direction), aRv); michael@0: } michael@0: michael@0: inline nsresult michael@0: CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream) 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", "CopyData"); michael@0: michael@0: nsresult rv; michael@0: michael@0: do { michael@0: char copyBuffer[FILE_COPY_BUFFER_SIZE]; michael@0: michael@0: uint32_t numRead; michael@0: rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!numRead) { michael@0: break; michael@0: } michael@0: michael@0: uint32_t numWrite; michael@0: rv = aOutputStream->Write(copyBuffer, numRead, &numWrite); michael@0: if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { michael@0: rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE); michael@0: } while (true); michael@0: michael@0: rv = aOutputStream->Flush(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: ObjectStoreHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mObjectStore = nullptr; michael@0: AsyncConnectionHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: ObjectStoreHelper::Dispatch(nsIEventTarget* aDatabaseThread) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", "ObjectStoreHelper::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 (mObjectStore->Transaction()->Database()->IsInvalidated()) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: IndexedDBObjectStoreChild* objectStoreActor = mObjectStore->GetActorChild(); michael@0: NS_ASSERTION(objectStoreActor, "Must have an actor here!"); michael@0: michael@0: ObjectStoreRequestParams 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 = michael@0: new IndexedDBObjectStoreRequestChild(this, mObjectStore, params.type()); michael@0: objectStoreActor->SendPIndexedDBRequestConstructor(mActor, params); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: NoRequestObjectStoreHelper::ReleaseMainThreadObjects() michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: mObjectStore = nullptr; michael@0: AsyncConnectionHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: NoRequestObjectStoreHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: NoRequestObjectStoreHelper::SendResponseToChildProcess(nsresult aResultCode) michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: return Success_NotSent; michael@0: } michael@0: michael@0: nsresult michael@0: NoRequestObjectStoreHelper::OnSuccess() michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: NoRequestObjectStoreHelper::OnError() michael@0: { michael@0: NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); michael@0: mTransaction->Abort(GetResultCode()); michael@0: } michael@0: michael@0: // This is a duplicate of the js engine's byte munging in StructuredClone.cpp michael@0: uint64_t michael@0: ReinterpretDoubleAsUInt64(double d) michael@0: { michael@0: union { michael@0: double d; michael@0: uint64_t u; michael@0: } pun; michael@0: pun.d = d; michael@0: return pun.u; michael@0: } michael@0: michael@0: nsresult michael@0: AddHelper::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", "AddHelper::DoDatabaseWork"); michael@0: michael@0: if (IndexedDatabaseManager::InLowDiskSpaceMode()) { michael@0: NS_WARNING("Refusing to add more data because disk space is low!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; michael@0: } michael@0: michael@0: nsresult rv; michael@0: bool keyUnset = mKey.IsUnset(); michael@0: int64_t osid = mObjectStore->Id(); michael@0: const KeyPath& keyPath = mObjectStore->GetKeyPath(); michael@0: michael@0: // The "|| keyUnset" here is mostly a debugging tool. If a key isn't michael@0: // specified we should never have a collision and so it shouldn't matter michael@0: // if we allow overwrite or not. By not allowing overwrite we raise michael@0: // detectable errors rather than corrupting data michael@0: nsCOMPtr stmt = !mOverwrite || keyUnset ? michael@0: mTransaction->GetCachedStatement( michael@0: "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " michael@0: "VALUES (:osid, :key_value, :data, :file_ids)") : michael@0: mTransaction->GetCachedStatement( michael@0: "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, " michael@0: "file_ids) " michael@0: "VALUES (:osid, :key_value, :data, :file_ids)"); michael@0: IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: mozStorageStatementScoper scoper(stmt); michael@0: michael@0: rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(), michael@0: "Should have key unless autoincrement"); michael@0: michael@0: int64_t autoIncrementNum = 0; michael@0: michael@0: if (mObjectStore->IsAutoIncrement()) { michael@0: if (keyUnset) { michael@0: autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId; michael@0: michael@0: MOZ_ASSERT(autoIncrementNum > 0, michael@0: "Generated key must always be a positive integer"); michael@0: michael@0: if (autoIncrementNum > (1LL << 53)) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: mKey.SetFromInteger(autoIncrementNum); michael@0: } michael@0: else if (mKey.IsFloat() && michael@0: mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) { michael@0: autoIncrementNum = floor(mKey.ToFloat()); michael@0: } michael@0: michael@0: if (keyUnset && keyPath.IsValid()) { michael@0: // Special case where someone put an object into an autoIncrement'ing michael@0: // objectStore with no key in its keyPath set. We needed to figure out michael@0: // which row id we would get above before we could set that properly. michael@0: michael@0: LittleEndian::writeUint64((char*)mCloneWriteInfo.mCloneBuffer.data() + michael@0: mCloneWriteInfo.mOffsetToKeyProp, michael@0: ReinterpretDoubleAsUInt64(static_cast( michael@0: autoIncrementNum))); michael@0: } michael@0: } michael@0: michael@0: mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); michael@0: michael@0: michael@0: // Compress the bytes before adding into the database. michael@0: const char* uncompressed = michael@0: reinterpret_cast(mCloneWriteInfo.mCloneBuffer.data()); michael@0: size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes(); michael@0: michael@0: // We don't have a smart pointer class that calls moz_free, so we need to michael@0: // manage | compressed | manually. michael@0: { michael@0: size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); michael@0: // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage michael@0: // expects to be able to free the adopted pointer with NS_Free. michael@0: char* compressed = (char*)moz_malloc(compressedLength); michael@0: NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: snappy::RawCompress(uncompressed, uncompressedLength, compressed, michael@0: &compressedLength); michael@0: michael@0: uint8_t* dataBuffer = reinterpret_cast(compressed); michael@0: size_t dataBufferLength = compressedLength; michael@0: michael@0: // If this call succeeds, | compressed | is now owned by the statement, and michael@0: // we are no longer responsible for it. michael@0: rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, michael@0: dataBufferLength); michael@0: if (NS_FAILED(rv)) { michael@0: moz_free(compressed); michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: } michael@0: michael@0: // Handle blobs michael@0: uint32_t length = mCloneWriteInfo.mFiles.Length(); michael@0: if (length) { michael@0: nsRefPtr fileManager = mDatabase->Manager(); michael@0: michael@0: nsCOMPtr directory = fileManager->GetDirectory(); michael@0: IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsCOMPtr journalDirectory = fileManager->EnsureJournalDirectory(); michael@0: IDB_ENSURE_TRUE(journalDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsAutoString fileIds; michael@0: michael@0: for (uint32_t index = 0; index < length; index++) { michael@0: StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index]; michael@0: michael@0: FileInfo* fileInfo = cloneFile.mFileInfo; michael@0: nsIInputStream* inputStream = cloneFile.mInputStream; michael@0: michael@0: int64_t id = fileInfo->Id(); michael@0: if (inputStream) { michael@0: // Create a journal file first michael@0: nsCOMPtr nativeFile = michael@0: fileManager->GetFileForId(journalDirectory, id); michael@0: IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); michael@0: IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: // Now we can copy the blob michael@0: nativeFile = fileManager->GetFileForId(directory, id); michael@0: IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: IDBDatabase* database = mObjectStore->Transaction()->Database(); michael@0: nsRefPtr outputStream = michael@0: FileOutputStream::Create(database->Type(), database->Group(), michael@0: database->Origin(), nativeFile); michael@0: IDB_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = CopyData(inputStream, outputStream); michael@0: if (NS_FAILED(rv) && michael@0: NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: cloneFile.mFile->AddFileInfo(fileInfo); michael@0: } michael@0: michael@0: if (index) { michael@0: fileIds.Append(NS_LITERAL_STRING(" ")); michael@0: } michael@0: fileIds.AppendInt(id); michael@0: } michael@0: michael@0: rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); michael@0: } michael@0: else { michael@0: rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = stmt->Execute(); michael@0: if (rv == NS_ERROR_STORAGE_CONSTRAINT) { michael@0: NS_ASSERTION(!keyUnset, "Generated key had a collision!?"); michael@0: return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: int64_t objectDataId; michael@0: rv = aConnection->GetLastInsertRowID(&objectDataId); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: // Update our indexes if needed. michael@0: if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) { michael@0: rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite, michael@0: objectDataId, mIndexUpdateInfo); michael@0: if (rv == NS_ERROR_STORAGE_CONSTRAINT) { michael@0: return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; michael@0: } michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: } michael@0: michael@0: if (autoIncrementNum) { michael@0: mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: AddHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: NS_ASSERTION(!mKey.IsUnset(), "Badness!"); michael@0: michael@0: mCloneWriteInfo.mCloneBuffer.clear(); michael@0: michael@0: return mKey.ToJSVal(aCx, aVal); michael@0: } michael@0: michael@0: void michael@0: AddHelper::ReleaseMainThreadObjects() michael@0: { michael@0: IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); michael@0: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: "AddHelper::PackArgumentsForParentProcess"); michael@0: michael@0: AddPutParams commonParams; michael@0: commonParams.cloneInfo() = mCloneWriteInfo; michael@0: commonParams.key() = mKey; michael@0: commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo); michael@0: michael@0: const nsTArray& files = mCloneWriteInfo.mFiles; michael@0: michael@0: if (!files.IsEmpty()) { michael@0: uint32_t fileCount = files.Length(); michael@0: michael@0: InfallibleTArray& blobsChild = commonParams.blobsChild(); michael@0: blobsChild.SetCapacity(fileCount); michael@0: michael@0: ContentChild* contentChild = ContentChild::GetSingleton(); michael@0: NS_ASSERTION(contentChild, "This should never be null!"); michael@0: michael@0: for (uint32_t index = 0; index < fileCount; index++) { michael@0: const StructuredCloneFile& file = files[index]; michael@0: michael@0: NS_ASSERTION(file.mFile, "This should never be null!"); michael@0: NS_ASSERTION(!file.mFileInfo, "This is not yet supported!"); michael@0: michael@0: BlobChild* actor = michael@0: contentChild->GetOrCreateActorForBlob(file.mFile); michael@0: if (!actor) { michael@0: IDB_REPORT_INTERNAL_ERR(); michael@0: return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: blobsChild.AppendElement(actor); michael@0: } michael@0: } michael@0: michael@0: if (mOverwrite) { michael@0: PutParams putParams; michael@0: putParams.commonParams() = commonParams; michael@0: aParams = putParams; michael@0: } michael@0: else { michael@0: AddParams addParams; michael@0: addParams.commonParams() = commonParams; michael@0: aParams = addParams; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: AddHelper::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: "AddHelper::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 if (mOverwrite) { michael@0: PutResponse putResponse; michael@0: putResponse.key() = mKey; michael@0: response = putResponse; michael@0: } michael@0: else { michael@0: AddResponse addResponse; michael@0: addResponse.key() = mKey; michael@0: response = addResponse; 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: AddHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TAddResponse || michael@0: aResponseValue.type() == ResponseValue::TPutResponse, michael@0: "Bad response type!"); michael@0: michael@0: mKey = mOverwrite ? michael@0: aResponseValue.get_PutResponse().key() : michael@0: aResponseValue.get_AddResponse().key(); michael@0: 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 [IDBObjectStore.cpp]"); michael@0: michael@0: nsCString keyRangeClause; michael@0: mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_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: "WHERE object_store_id = :osid") + michael@0: keyRangeClause + 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 = michael@0: stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->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_DOM_DATA_CLONE_ERR); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: GetHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mKeyRange = nullptr; michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: GetHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: "[IDBObjectStore.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: "[IDBObjectStore.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 = mObjectStore->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: 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: DeleteHelper::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", "DeleteHelper::DoDatabaseWork"); michael@0: michael@0: nsCString keyRangeClause; michael@0: mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); michael@0: michael@0: NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data " michael@0: "WHERE object_store_id = :osid") + 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("osid"), michael@0: mObjectStore->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: rv = stmt->Execute(); 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: DeleteHelper::GetSuccessResult(JSContext* aCx, michael@0: JS::MutableHandle aVal) michael@0: { michael@0: aVal.setUndefined(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DeleteHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: "DeleteHelper::PackArgumentsForParentProcess"); michael@0: michael@0: DeleteParams 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: DeleteHelper::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: "DeleteHelper::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: response = DeleteResponse(); 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: DeleteHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TDeleteResponse, michael@0: "Bad response type!"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ClearHelper::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", "ClearHelper::DoDatabaseWork"); michael@0: michael@0: nsCOMPtr stmt = michael@0: mTransaction->GetCachedStatement( michael@0: NS_LITERAL_CSTRING("DELETE FROM object_data " michael@0: "WHERE object_store_id = :osid")); 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("osid"), michael@0: mObjectStore->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = stmt->Execute(); 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: ClearHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: "ClearHelper::PackArgumentsForParentProcess"); michael@0: michael@0: aParams = ClearParams(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: AsyncConnectionHelper::ChildProcessSendResult michael@0: ClearHelper::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: "ClearHelper::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: response = ClearResponse(); 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: ClearHelper::UnpackResponseFromParentProcess( michael@0: const ResponseValue& aResponseValue) michael@0: { michael@0: NS_ASSERTION(aResponseValue.type() == ResponseValue::TClearResponse, michael@0: "Bad response type!"); 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: michael@0: PROFILER_LABEL("IndexedDB", michael@0: "OpenCursorHelper::DoDatabaseWork [IDBObjectStore.cpp]"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); michael@0: michael@0: nsCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(keyValue, keyRangeClause); michael@0: } michael@0: michael@0: nsAutoCString directionClause; michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: directionClause.AssignLiteral(" ORDER BY key_value ASC"); michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: directionClause.AssignLiteral(" ORDER BY key_value DESC"); michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown direction type!"); michael@0: } michael@0: michael@0: nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids " michael@0: "FROM object_data " michael@0: "WHERE object_store_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"), michael@0: mObjectStore->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 = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, 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: keyRangeClause.Truncate(); michael@0: nsAutoCString continueToKeyRangeClause; michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); michael@0: NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); michael@0: michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: AppendConditionClause(keyValue, currentKey, false, false, michael@0: keyRangeClause); michael@0: AppendConditionClause(keyValue, currentKey, false, true, michael@0: continueToKeyRangeClause); michael@0: if (mKeyRange && !mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(keyValue, rangeKey, true, michael@0: !mKeyRange->IsUpperOpen(), keyRangeClause); michael@0: AppendConditionClause(keyValue, rangeKey, true, michael@0: !mKeyRange->IsUpperOpen(), michael@0: continueToKeyRangeClause); michael@0: mRangeKey = mKeyRange->Upper(); michael@0: } michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); michael@0: AppendConditionClause(keyValue, currentKey, true, true, michael@0: continueToKeyRangeClause); michael@0: if (mKeyRange && !mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(keyValue, rangeKey, false, michael@0: !mKeyRange->IsLowerOpen(), keyRangeClause); michael@0: AppendConditionClause(keyValue, rangeKey, false, michael@0: !mKeyRange->IsLowerOpen(), michael@0: continueToKeyRangeClause); michael@0: mRangeKey = mKeyRange->Lower(); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown direction type!"); michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids " michael@0: "FROM object_data " michael@0: "WHERE object_store_id = :id"); michael@0: michael@0: mContinueQuery = queryStart + keyRangeClause + directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); michael@0: michael@0: mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + michael@0: NS_LITERAL_CSTRING(" LIMIT "); 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, mObjectStore, mDirection, michael@0: mRangeKey, mContinueQuery, mContinueToQuery, mKey, 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: nsresult michael@0: OpenCursorHelper::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: OpenCursorHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mKeyRange = nullptr; michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); michael@0: michael@0: mCursor = nullptr; 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: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: OpenCursorHelper::PackArgumentsForParentProcess( michael@0: ObjectStoreRequestParams& 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: "[IDBObjectStore.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: "[IDBObjectStore.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 = mObjectStore->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: IndexedDBObjectStoreParent* objectStoreActor = michael@0: mObjectStore->GetActorParent(); michael@0: NS_ASSERTION(objectStoreActor, "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: ObjectStoreCursorConstructorParams params; michael@0: params.requestParent() = requestActor; michael@0: params.direction() = mDirection; michael@0: params.key() = mKey; michael@0: params.optionalCloneInfo() = mSerializedCloneReadInfo; michael@0: params.blobsParent().SwapElements(blobsParent); michael@0: michael@0: if (!objectStoreActor->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: OpenCursorHelper::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: OpenKeyCursorHelper::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: "OpenKeyCursorHelper::DoDatabaseWork [IDBObjectStore.cpp]"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); michael@0: NS_NAMED_LITERAL_CSTRING(id, "id"); michael@0: NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); michael@0: michael@0: nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT ") + keyValue + michael@0: NS_LITERAL_CSTRING(" FROM object_data WHERE " michael@0: "object_store_id = :") + michael@0: id; michael@0: michael@0: nsAutoCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(keyValue, keyRangeClause); michael@0: } michael@0: michael@0: nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: directionClause.AppendLiteral(" ASC"); michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: directionClause.AppendLiteral(" DESC"); michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown direction type!"); michael@0: } michael@0: michael@0: nsCString firstQuery = queryStart + keyRangeClause + directionClause + michael@0: openLimit + NS_LITERAL_CSTRING("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(id, mObjectStore->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: // Now we need to make the query to get the next match. michael@0: keyRangeClause.Truncate(); michael@0: nsAutoCString continueToKeyRangeClause; michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); michael@0: NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); michael@0: michael@0: switch (mDirection) { michael@0: case IDBCursor::NEXT: michael@0: case IDBCursor::NEXT_UNIQUE: michael@0: AppendConditionClause(keyValue, currentKey, false, false, michael@0: keyRangeClause); michael@0: AppendConditionClause(keyValue, currentKey, false, true, michael@0: continueToKeyRangeClause); michael@0: if (mKeyRange && !mKeyRange->Upper().IsUnset()) { michael@0: AppendConditionClause(keyValue, rangeKey, true, michael@0: !mKeyRange->IsUpperOpen(), keyRangeClause); michael@0: AppendConditionClause(keyValue, rangeKey, true, michael@0: !mKeyRange->IsUpperOpen(), michael@0: continueToKeyRangeClause); michael@0: mRangeKey = mKeyRange->Upper(); michael@0: } michael@0: break; michael@0: michael@0: case IDBCursor::PREV: michael@0: case IDBCursor::PREV_UNIQUE: michael@0: AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); michael@0: AppendConditionClause(keyValue, currentKey, true, true, michael@0: continueToKeyRangeClause); michael@0: if (mKeyRange && !mKeyRange->Lower().IsUnset()) { michael@0: AppendConditionClause(keyValue, rangeKey, false, michael@0: !mKeyRange->IsLowerOpen(), keyRangeClause); michael@0: AppendConditionClause(keyValue, rangeKey, false, michael@0: !mKeyRange->IsLowerOpen(), michael@0: continueToKeyRangeClause); michael@0: mRangeKey = mKeyRange->Lower(); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown direction type!"); michael@0: } michael@0: michael@0: mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit; michael@0: mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + michael@0: openLimit; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::EnsureCursor() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "OpenKeyCursorHelper::EnsureCursor " michael@0: "[IDBObjectStore.cpp]"); michael@0: michael@0: if (mCursor || mKey.IsUnset()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection, michael@0: mRangeKey, mContinueQuery, mContinueToQuery, michael@0: mKey); michael@0: IDB_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: 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: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "OpenKeyCursorHelper::GetSuccessResult " michael@0: "[IDBObjectStore.cpp]"); 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: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mKeyRange = nullptr; michael@0: mCursor = nullptr; michael@0: michael@0: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: OpenKeyCursorHelper::PackArgumentsForParentProcess( michael@0: ObjectStoreRequestParams& 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: "OpenKeyCursorHelper::" michael@0: "PackArgumentsForParentProcess " michael@0: "[IDBObjectStore.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: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); michael@0: MOZ_ASSERT(!mCursor); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "OpenKeyCursorHelper::SendResponseToChildProcess " michael@0: "[IDBObjectStore.cpp]"); michael@0: michael@0: IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); michael@0: MOZ_ASSERT(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: } else { michael@0: OpenCursorResponse openCursorResponse; michael@0: michael@0: if (!mCursor) { michael@0: openCursorResponse = mozilla::void_t(); michael@0: } michael@0: else { michael@0: IndexedDBObjectStoreParent* objectStoreActor = michael@0: mObjectStore->GetActorParent(); michael@0: MOZ_ASSERT(objectStoreActor); michael@0: michael@0: IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); michael@0: MOZ_ASSERT(requestActor); michael@0: michael@0: ObjectStoreCursorConstructorParams params; michael@0: params.requestParent() = requestActor; michael@0: params.direction() = mDirection; michael@0: params.key() = mKey; michael@0: params.optionalCloneInfo() = mozilla::void_t(); michael@0: michael@0: if (!objectStoreActor->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: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); michael@0: MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse); michael@0: MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() == michael@0: OpenCursorResponse::Tvoid_t || michael@0: aResponseValue.get_OpenCursorResponse().type() == michael@0: OpenCursorResponse::TPIndexedDBCursorChild); michael@0: MOZ_ASSERT(!mCursor); michael@0: michael@0: PROFILER_MAIN_THREAD_LABEL("IndexedDB", michael@0: "OpenKeyCursorHelper::" michael@0: "UnpackResponseFromParentProcess " michael@0: "[IDBObjectStore.cpp]"); 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("Unknown response union type!"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CreateIndexHelper::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", "CreateIndexHelper::DoDatabaseWork"); michael@0: michael@0: if (IndexedDatabaseManager::InLowDiskSpaceMode()) { michael@0: NS_WARNING("Refusing to create index because disk space is low!"); michael@0: return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; michael@0: } michael@0: michael@0: // Insert the data into the database. michael@0: nsCOMPtr stmt = michael@0: mTransaction->GetCachedStatement( michael@0: "INSERT INTO object_store_index (id, name, key_path, unique_index, " michael@0: "multientry, object_store_id) " michael@0: "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)" michael@0: ); 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"), michael@0: mIndex->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: nsAutoString keyPathSerialization; michael@0: mIndex->GetKeyPath().SerializeToString(keyPathSerialization); michael@0: rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), michael@0: keyPathSerialization); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"), michael@0: mIndex->IsUnique() ? 1 : 0); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"), michael@0: mIndex->IsMultiEntry() ? 1 : 0); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), michael@0: mIndex->ObjectStore()->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (NS_FAILED(stmt->Execute())) { michael@0: return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: int64_t id; michael@0: aConnection->GetLastInsertRowID(&id); michael@0: NS_ASSERTION(mIndex->Id() == id, "Bad index id!"); michael@0: } michael@0: #endif michael@0: michael@0: // Now we need to populate the index with data from the object store. michael@0: rv = InsertDataFromObjectStore(aConnection); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: CreateIndexHelper::ReleaseMainThreadObjects() michael@0: { michael@0: mIndex = nullptr; michael@0: NoRequestObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection) michael@0: { michael@0: nsCOMPtr stmt = michael@0: mTransaction->GetCachedStatement( michael@0: NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM " michael@0: "object_data WHERE object_store_id = :osid")); 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("osid"), michael@0: mIndex->ObjectStore()->Id()); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: IDB_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, michael@0: NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); 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: if (!hasResult) { michael@0: // Bail early if we have no data to avoid creating the below runtime michael@0: return NS_OK; michael@0: } michael@0: michael@0: ThreadLocalJSRuntime* tlsEntry = michael@0: reinterpret_cast(PR_GetThreadPrivate(sTLSIndex)); michael@0: michael@0: if (!tlsEntry) { michael@0: tlsEntry = ThreadLocalJSRuntime::Create(); michael@0: IDB_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: PR_SetThreadPrivate(sTLSIndex, tlsEntry); michael@0: } michael@0: michael@0: JSContext* cx = tlsEntry->Context(); michael@0: JSAutoRequest ar(cx); michael@0: JSAutoCompartment ac(cx, tlsEntry->Global()); michael@0: michael@0: do { michael@0: StructuredCloneReadInfo cloneReadInfo; michael@0: rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, michael@0: mDatabase, cloneReadInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer; michael@0: michael@0: JSStructuredCloneCallbacks callbacks = { michael@0: IDBObjectStore::StructuredCloneReadCallback, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr michael@0: }; michael@0: michael@0: JS::Rooted clone(cx); michael@0: if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) { michael@0: NS_WARNING("Failed to deserialize structured clone data!"); michael@0: return NS_ERROR_DOM_DATA_CLONE_ERR; michael@0: } michael@0: michael@0: nsTArray updateInfo; michael@0: rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(), michael@0: mIndex->GetKeyPath(), michael@0: mIndex->IsUnique(), michael@0: mIndex->IsMultiEntry(), michael@0: tlsEntry->Context(), michael@0: clone, updateInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: int64_t objectDataID = stmt->AsInt64(0); michael@0: michael@0: Key key; michael@0: rv = key.SetFromStatement(stmt, 3); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(), michael@0: key, false, objectDataID, updateInfo); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); 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: void michael@0: CreateIndexHelper::DestroyTLSEntry(void* aPtr) michael@0: { michael@0: delete reinterpret_cast(aPtr); michael@0: } michael@0: michael@0: nsresult michael@0: DeleteIndexHelper::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", "DeleteIndexHelper::DoDatabaseWork"); michael@0: michael@0: nsCOMPtr stmt = michael@0: mTransaction->GetCachedStatement( michael@0: "DELETE FROM object_store_index " michael@0: "WHERE name = :name " michael@0: ); 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->BindStringByName(NS_LITERAL_CSTRING("name"), mName); michael@0: IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: if (NS_FAILED(stmt->Execute())) { michael@0: return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; michael@0: } michael@0: 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", michael@0: "GetAllHelper::DoDatabaseWork [IDBObjectStore.cpp]"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); michael@0: NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); michael@0: michael@0: nsAutoCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: if (!mKeyRange->Lower().IsUnset()) { michael@0: keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); michael@0: if (mKeyRange->IsLowerOpen()) { michael@0: keyRangeClause.AppendLiteral(" > :"); michael@0: } michael@0: else { michael@0: keyRangeClause.AppendLiteral(" >= :"); michael@0: } michael@0: keyRangeClause.Append(lowerKeyName); michael@0: } michael@0: michael@0: if (!mKeyRange->Upper().IsUnset()) { michael@0: keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); michael@0: if (mKeyRange->IsUpperOpen()) { michael@0: keyRangeClause.AppendLiteral(" < :"); michael@0: } michael@0: else { michael@0: keyRangeClause.AppendLiteral(" <= :"); michael@0: } michael@0: keyRangeClause.Append(upperKeyName); michael@0: } michael@0: } michael@0: michael@0: nsAutoCString limitClause; michael@0: if (mLimit != UINT32_MAX) { michael@0: limitClause.AssignLiteral(" 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: "WHERE object_store_id = :osid") + michael@0: keyRangeClause + michael@0: NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + michael@0: limitClause; michael@0: michael@0: mCloneReadInfos.SetCapacity(50); 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("osid"), michael@0: mObjectStore->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: 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, "Shouldn't fail since SetCapacity succeeded!"); 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: mKeyRange = nullptr; michael@0: for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { michael@0: IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); michael@0: } michael@0: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: GetAllHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: "[IDBObjectStore.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: "[IDBObjectStore.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: if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) { michael@0: IDBDatabase* database = mObjectStore->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: // Append the structured clone data. michael@0: const StructuredCloneReadInfo& clone = mCloneReadInfos[index]; michael@0: SerializedStructuredCloneReadInfo* info = infos.AppendElement(); michael@0: *info = clone; michael@0: michael@0: // Now take care of the files. michael@0: const nsTArray& files = clone.mFiles; michael@0: BlobArray* blobArray = blobArrays.AppendElement(); 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: 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 [IDObjectStore.cpp]"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); michael@0: michael@0: nsAutoCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: mKeyRange->GetBindingClause(keyValue, keyRangeClause); michael@0: } michael@0: michael@0: nsAutoCString 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: NS_NAMED_LITERAL_CSTRING(osid, "osid"); michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyValue + michael@0: NS_LITERAL_CSTRING(" FROM object_data WHERE " michael@0: "object_store_id = :") + michael@0: osid + keyRangeClause + michael@0: NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + michael@0: 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(osid, mObjectStore->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: "[IDBObjectStore.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, 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: MOZ_ASSERT(!key.IsUnset()); 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: void michael@0: GetAllKeysHelper::ReleaseMainThreadObjects() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: mKeyRange = nullptr; michael@0: michael@0: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: GetAllKeysHelper::PackArgumentsForParentProcess( michael@0: ObjectStoreRequestParams& 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: "[IDBObjectStore.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: } 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: "[IDBObjectStore.cpp]"); michael@0: michael@0: IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); michael@0: MOZ_ASSERT(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: 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", michael@0: "CountHelper::DoDatabaseWork [IDBObjectStore.cpp]"); michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); michael@0: NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); michael@0: michael@0: nsAutoCString keyRangeClause; michael@0: if (mKeyRange) { michael@0: if (!mKeyRange->Lower().IsUnset()) { michael@0: keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); michael@0: if (mKeyRange->IsLowerOpen()) { michael@0: keyRangeClause.AppendLiteral(" > :"); michael@0: } michael@0: else { michael@0: keyRangeClause.AppendLiteral(" >= :"); michael@0: } michael@0: keyRangeClause.Append(lowerKeyName); michael@0: } michael@0: michael@0: if (!mKeyRange->Upper().IsUnset()) { michael@0: keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); michael@0: if (mKeyRange->IsUpperOpen()) { michael@0: keyRangeClause.AppendLiteral(" < :"); michael@0: } michael@0: else { michael@0: keyRangeClause.AppendLiteral(" <= :"); michael@0: } michael@0: keyRangeClause.Append(upperKeyName); michael@0: } michael@0: } michael@0: michael@0: nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data " michael@0: "WHERE object_store_id = :osid") + 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("osid"), michael@0: mObjectStore->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: ObjectStoreHelper::ReleaseMainThreadObjects(); michael@0: } michael@0: michael@0: nsresult michael@0: CountHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& 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: "[IDBObjectStore.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: "[IDBObjectStore.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: }