Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / dom / indexedDB / IDBObjectStore.cpp
blob554d64c40de434ad7c07c7337d9b8dedc553bf64
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/basictypes.h"
9 #include "IDBObjectStore.h"
11 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
12 #include "nsIOutputStream.h"
14 #include <algorithm>
15 #include "jsfriendapi.h"
16 #include "mozilla/dom/ContentChild.h"
17 #include "mozilla/dom/IDBMutableFileBinding.h"
18 #include "mozilla/dom/nsIContentParent.h"
19 #include "mozilla/dom/StructuredCloneTags.h"
20 #include "mozilla/dom/TabChild.h"
21 #include "mozilla/dom/ipc/Blob.h"
22 #include "mozilla/dom/quota/FileStreams.h"
23 #include "mozilla/Endian.h"
24 #include "mozilla/storage.h"
25 #include "nsContentUtils.h"
26 #include "nsDOMClassInfo.h"
27 #include "nsDOMFile.h"
28 #include "mozilla/dom/DOMStringList.h"
29 #include "nsJSUtils.h"
30 #include "nsServiceManagerUtils.h"
31 #include "nsThreadUtils.h"
32 #include "snappy/snappy.h"
34 #include "AsyncConnectionHelper.h"
35 #include "IDBCursor.h"
36 #include "IDBEvents.h"
37 #include "IDBIndex.h"
38 #include "IDBKeyRange.h"
39 #include "IDBMutableFile.h"
40 #include "IDBTransaction.h"
41 #include "DatabaseInfo.h"
42 #include "KeyPath.h"
43 #include "ProfilerHelpers.h"
44 #include "ReportInternalError.h"
46 #include "ipc/IndexedDBChild.h"
47 #include "ipc/IndexedDBParent.h"
49 #include "IndexedDatabaseInlines.h"
50 #include "nsCharSeparatedTokenizer.h"
52 #define FILE_COPY_BUFFER_SIZE 32768
54 USING_INDEXEDDB_NAMESPACE
55 using namespace mozilla::dom;
56 using namespace mozilla::dom::indexedDB::ipc;
57 using mozilla::dom::quota::FileOutputStream;
58 using mozilla::ErrorResult;
59 using mozilla::fallible_t;
60 using mozilla::LittleEndian;
61 using mozilla::Move;
62 using mozilla::NativeEndian;
64 BEGIN_INDEXEDDB_NAMESPACE
66 struct MutableFileData
68 nsString type;
69 nsString name;
72 struct BlobOrFileData
74 BlobOrFileData()
75 : tag(0), size(0), lastModifiedDate(UINT64_MAX)
76 { }
78 uint32_t tag;
79 uint64_t size;
80 nsString type;
81 nsString name;
82 uint64_t lastModifiedDate;
85 END_INDEXEDDB_NAMESPACE
87 namespace {
89 inline
90 bool
91 IgnoreNothing(char16_t c)
93 return false;
96 class ObjectStoreHelper : public AsyncConnectionHelper
98 public:
99 ObjectStoreHelper(IDBTransaction* aTransaction,
100 IDBRequest* aRequest,
101 IDBObjectStore* aObjectStore)
102 : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
103 mActor(nullptr)
105 NS_ASSERTION(aTransaction, "Null transaction!");
106 NS_ASSERTION(aRequest, "Null request!");
107 NS_ASSERTION(aObjectStore, "Null object store!");
110 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
112 virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE;
114 virtual nsresult
115 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
116 nsIContentChild* aBlobCreator) = 0;
118 virtual nsresult
119 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0;
121 protected:
122 nsRefPtr<IDBObjectStore> mObjectStore;
124 private:
125 IndexedDBObjectStoreRequestChild* mActor;
128 class NoRequestObjectStoreHelper : public AsyncConnectionHelper
130 public:
131 NoRequestObjectStoreHelper(IDBTransaction* aTransaction,
132 IDBObjectStore* aObjectStore)
133 : AsyncConnectionHelper(aTransaction, nullptr), mObjectStore(aObjectStore)
135 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
136 NS_ASSERTION(aTransaction, "Null transaction!");
137 NS_ASSERTION(aObjectStore, "Null object store!");
140 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
142 virtual nsresult UnpackResponseFromParentProcess(
143 const ResponseValue& aResponseValue)
144 MOZ_OVERRIDE;
146 virtual ChildProcessSendResult
147 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
149 virtual nsresult OnSuccess() MOZ_OVERRIDE;
151 virtual void OnError() MOZ_OVERRIDE;
153 protected:
154 nsRefPtr<IDBObjectStore> mObjectStore;
157 class AddHelper : public ObjectStoreHelper
159 public:
160 AddHelper(IDBTransaction* aTransaction,
161 IDBRequest* aRequest,
162 IDBObjectStore* aObjectStore,
163 StructuredCloneWriteInfo&& aCloneWriteInfo,
164 const Key& aKey,
165 bool aOverwrite,
166 nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
167 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
168 mCloneWriteInfo(Move(aCloneWriteInfo)),
169 mKey(aKey),
170 mOverwrite(aOverwrite)
172 mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
175 ~AddHelper()
177 IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
180 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
181 MOZ_OVERRIDE;
183 virtual nsresult GetSuccessResult(JSContext* aCx,
184 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
186 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
188 virtual nsresult
189 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
190 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
192 virtual ChildProcessSendResult
193 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
195 virtual nsresult
196 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
197 MOZ_OVERRIDE;
199 private:
200 // These may change in the autoincrement case.
201 StructuredCloneWriteInfo mCloneWriteInfo;
202 Key mKey;
203 nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
204 const bool mOverwrite;
207 class GetHelper : public ObjectStoreHelper
209 public:
210 GetHelper(IDBTransaction* aTransaction,
211 IDBRequest* aRequest,
212 IDBObjectStore* aObjectStore,
213 IDBKeyRange* aKeyRange)
214 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
215 mKeyRange(aKeyRange)
217 NS_ASSERTION(aKeyRange, "Null key range!");
220 ~GetHelper()
222 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
225 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
226 MOZ_OVERRIDE;
228 virtual nsresult GetSuccessResult(JSContext* aCx,
229 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
231 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
233 virtual nsresult
234 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
235 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
237 virtual ChildProcessSendResult
238 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
240 virtual nsresult
241 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
242 MOZ_OVERRIDE;
244 protected:
245 // In-params.
246 nsRefPtr<IDBKeyRange> mKeyRange;
248 private:
249 // Out-params.
250 StructuredCloneReadInfo mCloneReadInfo;
253 class DeleteHelper : public GetHelper
255 public:
256 DeleteHelper(IDBTransaction* aTransaction,
257 IDBRequest* aRequest,
258 IDBObjectStore* aObjectStore,
259 IDBKeyRange* aKeyRange)
260 : GetHelper(aTransaction, aRequest, aObjectStore, aKeyRange)
263 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
264 MOZ_OVERRIDE;
266 virtual nsresult GetSuccessResult(JSContext* aCx,
267 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
269 virtual nsresult
270 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
271 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
273 virtual ChildProcessSendResult
274 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
276 virtual nsresult
277 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
278 MOZ_OVERRIDE;
281 class ClearHelper : public ObjectStoreHelper
283 public:
284 ClearHelper(IDBTransaction* aTransaction,
285 IDBRequest* aRequest,
286 IDBObjectStore* aObjectStore)
287 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore)
290 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
291 MOZ_OVERRIDE;
293 virtual nsresult
294 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
295 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
297 virtual ChildProcessSendResult
298 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
300 virtual nsresult
301 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
302 MOZ_OVERRIDE;
305 class OpenCursorHelper : public ObjectStoreHelper
307 public:
308 OpenCursorHelper(IDBTransaction* aTransaction,
309 IDBRequest* aRequest,
310 IDBObjectStore* aObjectStore,
311 IDBKeyRange* aKeyRange,
312 IDBCursor::Direction aDirection)
313 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
314 mKeyRange(aKeyRange), mDirection(aDirection)
317 ~OpenCursorHelper()
319 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
322 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
323 MOZ_OVERRIDE;
325 virtual nsresult GetSuccessResult(JSContext* aCx,
326 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
328 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
330 virtual nsresult
331 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
332 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
334 virtual ChildProcessSendResult
335 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
337 virtual nsresult
338 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
339 MOZ_OVERRIDE;
341 private:
342 nsresult EnsureCursor();
344 // In-params.
345 nsRefPtr<IDBKeyRange> mKeyRange;
346 const IDBCursor::Direction mDirection;
348 // Out-params.
349 Key mKey;
350 StructuredCloneReadInfo mCloneReadInfo;
351 nsCString mContinueQuery;
352 nsCString mContinueToQuery;
353 Key mRangeKey;
355 // Only used in the parent process.
356 nsRefPtr<IDBCursor> mCursor;
357 SerializedStructuredCloneReadInfo mSerializedCloneReadInfo;
360 class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper
362 public:
363 OpenKeyCursorHelper(IDBTransaction* aTransaction,
364 IDBRequest* aRequest,
365 IDBObjectStore* aObjectStore,
366 IDBKeyRange* aKeyRange,
367 IDBCursor::Direction aDirection)
368 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
369 mKeyRange(aKeyRange), mDirection(aDirection)
372 virtual nsresult
373 DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
375 virtual nsresult
376 GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
377 MOZ_OVERRIDE;
379 virtual void
380 ReleaseMainThreadObjects() MOZ_OVERRIDE;
382 virtual nsresult
383 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
384 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
386 virtual ChildProcessSendResult
387 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
389 virtual nsresult
390 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
391 MOZ_OVERRIDE;
393 private:
394 ~OpenKeyCursorHelper()
397 nsresult EnsureCursor();
399 // In-params.
400 nsRefPtr<IDBKeyRange> mKeyRange;
401 const IDBCursor::Direction mDirection;
403 // Out-params.
404 Key mKey;
405 nsCString mContinueQuery;
406 nsCString mContinueToQuery;
407 Key mRangeKey;
409 // Only used in the parent process.
410 nsRefPtr<IDBCursor> mCursor;
413 class CreateIndexHelper : public NoRequestObjectStoreHelper
415 public:
416 CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex)
417 : NoRequestObjectStoreHelper(aTransaction, aIndex->ObjectStore()),
418 mIndex(aIndex)
420 if (sTLSIndex == BAD_TLS_INDEX) {
421 PR_NewThreadPrivateIndex(&sTLSIndex, DestroyTLSEntry);
424 NS_ASSERTION(sTLSIndex != BAD_TLS_INDEX,
425 "PR_NewThreadPrivateIndex failed!");
428 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
429 MOZ_OVERRIDE;
431 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
433 private:
434 nsresult InsertDataFromObjectStore(mozIStorageConnection* aConnection);
436 static void DestroyTLSEntry(void* aPtr);
438 static unsigned sTLSIndex;
440 // In-params.
441 nsRefPtr<IDBIndex> mIndex;
444 unsigned CreateIndexHelper::sTLSIndex = unsigned(BAD_TLS_INDEX);
446 class DeleteIndexHelper : public NoRequestObjectStoreHelper
448 public:
449 DeleteIndexHelper(IDBTransaction* aTransaction,
450 IDBObjectStore* aObjectStore,
451 const nsAString& aName)
452 : NoRequestObjectStoreHelper(aTransaction, aObjectStore), mName(aName)
455 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
456 MOZ_OVERRIDE;
458 private:
459 // In-params
460 nsString mName;
463 class GetAllHelper : public ObjectStoreHelper
465 public:
466 GetAllHelper(IDBTransaction* aTransaction,
467 IDBRequest* aRequest,
468 IDBObjectStore* aObjectStore,
469 IDBKeyRange* aKeyRange,
470 const uint32_t aLimit)
471 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
472 mKeyRange(aKeyRange), mLimit(aLimit)
475 ~GetAllHelper()
477 for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) {
478 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]);
482 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
483 MOZ_OVERRIDE;
485 virtual nsresult GetSuccessResult(JSContext* aCx,
486 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
488 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
490 virtual nsresult
491 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
492 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
494 virtual ChildProcessSendResult
495 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
497 virtual nsresult
498 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
499 MOZ_OVERRIDE;
501 protected:
502 // In-params.
503 nsRefPtr<IDBKeyRange> mKeyRange;
504 const uint32_t mLimit;
506 private:
507 // Out-params.
508 nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
511 class GetAllKeysHelper MOZ_FINAL : public ObjectStoreHelper
513 public:
514 GetAllKeysHelper(IDBTransaction* aTransaction,
515 IDBRequest* aRequest,
516 IDBObjectStore* aObjectStore,
517 IDBKeyRange* aKeyRange,
518 const uint32_t aLimit)
519 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
520 mKeyRange(aKeyRange), mLimit(aLimit)
523 virtual nsresult
524 DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
526 virtual nsresult
527 GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
528 MOZ_OVERRIDE;
530 virtual void
531 ReleaseMainThreadObjects() MOZ_OVERRIDE;
533 virtual nsresult
534 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
535 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
537 virtual ChildProcessSendResult
538 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
540 virtual nsresult
541 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
542 MOZ_OVERRIDE;
544 private:
545 ~GetAllKeysHelper()
548 nsRefPtr<IDBKeyRange> mKeyRange;
549 const uint32_t mLimit;
550 nsTArray<Key> mKeys;
553 class CountHelper : public ObjectStoreHelper
555 public:
556 CountHelper(IDBTransaction* aTransaction,
557 IDBRequest* aRequest,
558 IDBObjectStore* aObjectStore,
559 IDBKeyRange* aKeyRange)
560 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
561 mKeyRange(aKeyRange), mCount(0)
564 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
565 MOZ_OVERRIDE;
567 virtual nsresult GetSuccessResult(JSContext* aCx,
568 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
570 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
572 virtual nsresult
573 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
574 nsIContentChild* aBlobCreator) MOZ_OVERRIDE;
576 virtual ChildProcessSendResult
577 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
579 virtual nsresult
580 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
581 MOZ_OVERRIDE;
583 private:
584 nsRefPtr<IDBKeyRange> mKeyRange;
585 uint64_t mCount;
588 class MOZ_STACK_CLASS AutoRemoveIndex
590 public:
591 AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo,
592 const nsAString& aIndexName)
593 : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName)
596 ~AutoRemoveIndex()
598 if (mObjectStoreInfo) {
599 for (uint32_t i = 0; i < mObjectStoreInfo->indexes.Length(); i++) {
600 if (mObjectStoreInfo->indexes[i].name == mIndexName) {
601 mObjectStoreInfo->indexes.RemoveElementAt(i);
602 break;
608 void forget()
610 mObjectStoreInfo = nullptr;
613 private:
614 ObjectStoreInfo* mObjectStoreInfo;
615 nsString mIndexName;
618 class ThreadLocalJSRuntime
620 JSRuntime* mRuntime;
621 JSContext* mContext;
622 JSObject* mGlobal;
624 static const JSClass sGlobalClass;
625 static const unsigned sRuntimeHeapSize = 768 * 1024;
627 ThreadLocalJSRuntime()
628 : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr)
630 MOZ_COUNT_CTOR(ThreadLocalJSRuntime);
633 nsresult Init()
635 mRuntime = JS_NewRuntime(sRuntimeHeapSize);
636 NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY);
639 * Not setting this will cause JS_CHECK_RECURSION to report false
640 * positives
642 JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024);
644 mContext = JS_NewContext(mRuntime, 0);
645 NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY);
647 JSAutoRequest ar(mContext);
649 mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
650 JS::FireOnNewGlobalHook);
651 NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY);
653 return NS_OK;
656 public:
657 static ThreadLocalJSRuntime *Create()
659 ThreadLocalJSRuntime *entry = new ThreadLocalJSRuntime();
660 NS_ENSURE_TRUE(entry, nullptr);
662 if (NS_FAILED(entry->Init())) {
663 delete entry;
664 return nullptr;
667 return entry;
670 JSContext *Context() const
672 return mContext;
675 JSObject *Global() const
677 return mGlobal;
680 ~ThreadLocalJSRuntime()
682 MOZ_COUNT_DTOR(ThreadLocalJSRuntime);
684 if (mContext) {
685 JS_DestroyContext(mContext);
688 if (mRuntime) {
689 JS_DestroyRuntime(mRuntime);
694 const JSClass ThreadLocalJSRuntime::sGlobalClass = {
695 "IndexedDBTransactionThreadGlobal",
696 JSCLASS_GLOBAL_FLAGS,
697 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
698 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
699 nullptr, nullptr, nullptr, nullptr,
700 JS_GlobalObjectTraceHook
703 inline
704 already_AddRefed<IDBRequest>
705 GenerateRequest(IDBObjectStore* aObjectStore)
707 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
708 IDBDatabase* database = aObjectStore->Transaction()->Database();
709 return IDBRequest::Create(aObjectStore, database,
710 aObjectStore->Transaction());
713 struct MOZ_STACK_CLASS GetAddInfoClosure
715 IDBObjectStore* mThis;
716 StructuredCloneWriteInfo& mCloneWriteInfo;
717 JS::Handle<JS::Value> mValue;
720 nsresult
721 GetAddInfoCallback(JSContext* aCx, void* aClosure)
723 GetAddInfoClosure* data = static_cast<GetAddInfoClosure*>(aClosure);
725 data->mCloneWriteInfo.mOffsetToKeyProp = 0;
726 data->mCloneWriteInfo.mTransaction = data->mThis->Transaction();
728 if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo, data->mValue)) {
729 return NS_ERROR_DOM_DATA_CLONE_ERR;
732 return NS_OK;
735 inline
736 BlobChild*
737 ActorFromRemoteBlob(nsIDOMBlob* aBlob)
739 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
740 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
742 nsRefPtr<DOMFile> blob = static_cast<DOMFile*>(aBlob);
743 nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(blob->Impl());
744 if (remoteBlob) {
745 BlobChild* actor =
746 static_cast<BlobChild*>(static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
747 NS_ASSERTION(actor, "Null actor?!");
748 return actor;
750 return nullptr;
753 inline
754 bool
755 ResolveMysteryFile(nsIDOMBlob* aBlob, const nsString& aName,
756 const nsString& aContentType, uint64_t aSize,
757 uint64_t aLastModifiedDate)
759 BlobChild* actor = ActorFromRemoteBlob(aBlob);
760 if (actor) {
761 return actor->SetMysteryBlobInfo(aName, aContentType,
762 aSize, aLastModifiedDate);
764 return true;
767 inline
768 bool
769 ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType,
770 uint64_t aSize)
772 BlobChild* actor = ActorFromRemoteBlob(aBlob);
773 if (actor) {
774 return actor->SetMysteryBlobInfo(aContentType, aSize);
776 return true;
779 class MainThreadDeserializationTraits
781 public:
782 static bool CreateAndWrapMutableFile(JSContext* aCx,
783 IDBDatabase* aDatabase,
784 StructuredCloneFile& aFile,
785 const MutableFileData& aData,
786 JS::MutableHandle<JSObject*> aResult)
788 MOZ_ASSERT(NS_IsMainThread());
790 nsRefPtr<FileInfo>& fileInfo = aFile.mFileInfo;
792 nsRefPtr<IDBMutableFile> mutableFile = IDBMutableFile::Create(aData.name,
793 aData.type, aDatabase, fileInfo.forget());
795 JS::Rooted<JSObject*> result(aCx, mutableFile->WrapObject(aCx));
796 if (NS_WARN_IF(!result)) {
797 return false;
800 aResult.set(result);
801 return true;
804 static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx,
805 IDBDatabase* aDatabase,
806 StructuredCloneFile& aFile,
807 const BlobOrFileData& aData)
809 MOZ_ASSERT(NS_IsMainThread());
811 MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
812 aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
813 aData.tag == SCTAG_DOM_BLOB);
815 nsresult rv = NS_OK;
817 nsRefPtr<FileInfo>& fileInfo = aFile.mFileInfo;
819 nsCOMPtr<nsIFile> nativeFile;
820 if (!aFile.mFile) {
821 FileManager* fileManager = aDatabase->Manager();
822 NS_ASSERTION(fileManager, "This should never be null!");
824 nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
825 if (!directory) {
826 NS_WARNING("Failed to get directory!");
827 return nullptr;
830 nativeFile = fileManager->GetFileForId(directory, fileInfo->Id());
831 if (!nativeFile) {
832 NS_WARNING("Failed to get file!");
833 return nullptr;
837 if (aData.tag == SCTAG_DOM_BLOB) {
838 nsCOMPtr<nsIDOMBlob> domBlob;
839 if (aFile.mFile) {
840 if (!ResolveMysteryBlob(aFile.mFile, aData.type, aData.size)) {
841 return nullptr;
843 domBlob = aFile.mFile;
845 else {
846 domBlob = DOMFile::CreateFromFile(aData.type, aData.size, nativeFile,
847 fileInfo);
850 JS::Rooted<JS::Value> wrappedBlob(aCx);
851 rv = nsContentUtils::WrapNative(aCx, domBlob, &NS_GET_IID(nsIDOMBlob),
852 &wrappedBlob);
853 if (NS_FAILED(rv)) {
854 NS_WARNING("Failed to wrap native!");
855 return nullptr;
858 return wrappedBlob.toObjectOrNull();
861 nsCOMPtr<nsIDOMFile> domFile;
862 if (aFile.mFile) {
863 if (!ResolveMysteryFile(aFile.mFile, aData.name, aData.type, aData.size,
864 aData.lastModifiedDate)) {
865 return nullptr;
867 domFile = do_QueryInterface(aFile.mFile);
868 NS_ASSERTION(domFile, "This should never fail!");
870 else {
871 domFile = DOMFile::CreateFromFile(aData.name, aData.type, aData.size,
872 nativeFile, fileInfo);
875 JS::Rooted<JS::Value> wrappedFile(aCx);
876 rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile),
877 &wrappedFile);
878 if (NS_FAILED(rv)) {
879 NS_WARNING("Failed to wrap native!");
880 return nullptr;
883 return wrappedFile.toObjectOrNull();
888 class CreateIndexDeserializationTraits
890 public:
891 static bool CreateAndWrapMutableFile(JSContext* aCx,
892 IDBDatabase* aDatabase,
893 StructuredCloneFile& aFile,
894 const MutableFileData& aData,
895 JS::MutableHandle<JSObject*> aResult)
897 // MutableFile can't be used in index creation, so just make a dummy object.
898 JS::Rooted<JSObject*> obj(aCx,
899 JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
901 if (NS_WARN_IF(!obj)) {
902 return false;
905 aResult.set(obj);
906 return true;
909 static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx,
910 IDBDatabase* aDatabase,
911 StructuredCloneFile& aFile,
912 const BlobOrFileData& aData)
914 MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
915 aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
916 aData.tag == SCTAG_DOM_BLOB);
918 // The following properties are available for use in index creation
919 // Blob.size
920 // Blob.type
921 // File.name
922 // File.lastModifiedDate
924 JS::Rooted<JSObject*> obj(aCx,
925 JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
926 if (!obj) {
927 NS_WARNING("Failed to create object!");
928 return nullptr;
931 // Technically these props go on the proto, but this detail won't change
932 // the results of index creation.
934 JS::Rooted<JSString*> type(aCx,
935 JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length()));
936 if (!type ||
937 !JS_DefineProperty(aCx, obj, "size", double(aData.size), 0) ||
938 !JS_DefineProperty(aCx, obj, "type", type, 0)) {
939 return nullptr;
942 if (aData.tag == SCTAG_DOM_BLOB) {
943 return obj;
946 JS::Rooted<JSString*> name(aCx,
947 JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length()));
948 JS::Rooted<JSObject*> date(aCx,
949 JS_NewDateObjectMsec(aCx, aData.lastModifiedDate));
950 if (!name || !date ||
951 !JS_DefineProperty(aCx, obj, "name", name, 0) ||
952 !JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0)) {
953 return nullptr;
956 return obj;
960 } // anonymous namespace
962 const JSClass IDBObjectStore::sDummyPropJSClass = {
963 "dummy", 0,
964 JS_PropertyStub, JS_DeletePropertyStub,
965 JS_PropertyStub, JS_StrictPropertyStub,
966 JS_EnumerateStub, JS_ResolveStub,
967 JS_ConvertStub
970 // static
971 already_AddRefed<IDBObjectStore>
972 IDBObjectStore::Create(IDBTransaction* aTransaction,
973 ObjectStoreInfo* aStoreInfo,
974 const nsACString& aDatabaseId,
975 bool aCreating)
977 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
979 nsRefPtr<IDBObjectStore> objectStore = new IDBObjectStore();
981 objectStore->mTransaction = aTransaction;
982 objectStore->mName = aStoreInfo->name;
983 objectStore->mId = aStoreInfo->id;
984 objectStore->mKeyPath = aStoreInfo->keyPath;
985 objectStore->mAutoIncrement = aStoreInfo->autoIncrement;
986 objectStore->mDatabaseId = aDatabaseId;
987 objectStore->mInfo = aStoreInfo;
989 if (!IndexedDatabaseManager::IsMainProcess()) {
990 IndexedDBTransactionChild* transactionActor = aTransaction->GetActorChild();
991 NS_ASSERTION(transactionActor, "Must have an actor here!");
993 ObjectStoreConstructorParams params;
995 if (aCreating) {
996 CreateObjectStoreParams createParams;
997 createParams.info() = *aStoreInfo;
998 params = createParams;
1000 else {
1001 GetObjectStoreParams getParams;
1002 getParams.name() = aStoreInfo->name;
1003 params = getParams;
1006 IndexedDBObjectStoreChild* actor =
1007 new IndexedDBObjectStoreChild(objectStore);
1009 transactionActor->SendPIndexedDBObjectStoreConstructor(actor, params);
1012 return objectStore.forget();
1015 // static
1016 nsresult
1017 IDBObjectStore::AppendIndexUpdateInfo(
1018 int64_t aIndexID,
1019 const KeyPath& aKeyPath,
1020 bool aUnique,
1021 bool aMultiEntry,
1022 JSContext* aCx,
1023 JS::Handle<JS::Value> aVal,
1024 nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1026 nsresult rv;
1028 if (!aMultiEntry) {
1029 Key key;
1030 rv = aKeyPath.ExtractKey(aCx, aVal, key);
1032 // If an index's keypath doesn't match an object, we ignore that object.
1033 if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) {
1034 return NS_OK;
1037 if (NS_FAILED(rv)) {
1038 return rv;
1041 IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1042 updateInfo->indexId = aIndexID;
1043 updateInfo->indexUnique = aUnique;
1044 updateInfo->value = key;
1046 return NS_OK;
1049 JS::Rooted<JS::Value> val(aCx);
1050 if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
1051 return NS_OK;
1054 if (JS_IsArrayObject(aCx, val)) {
1055 JS::Rooted<JSObject*> array(aCx, &val.toObject());
1056 uint32_t arrayLength;
1057 if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
1058 IDB_REPORT_INTERNAL_ERR();
1059 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1062 for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
1063 JS::Rooted<JS::Value> arrayItem(aCx);
1064 if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) {
1065 IDB_REPORT_INTERNAL_ERR();
1066 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1069 Key value;
1070 if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
1071 value.IsUnset()) {
1072 // Not a value we can do anything with, ignore it.
1073 continue;
1076 IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1077 updateInfo->indexId = aIndexID;
1078 updateInfo->indexUnique = aUnique;
1079 updateInfo->value = value;
1082 else {
1083 Key value;
1084 if (NS_FAILED(value.SetFromJSVal(aCx, val)) ||
1085 value.IsUnset()) {
1086 // Not a value we can do anything with, ignore it.
1087 return NS_OK;
1090 IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1091 updateInfo->indexId = aIndexID;
1092 updateInfo->indexUnique = aUnique;
1093 updateInfo->value = value;
1096 return NS_OK;
1099 // static
1100 nsresult
1101 IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
1102 int64_t aObjectStoreId,
1103 const Key& aObjectStoreKey,
1104 bool aOverwrite,
1105 int64_t aObjectDataId,
1106 const nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1108 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
1109 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1111 PROFILER_LABEL("IDBObjectStore", "UpdateIndexes",
1112 js::ProfileEntry::Category::STORAGE);
1114 nsresult rv;
1116 NS_ASSERTION(aObjectDataId != INT64_MIN, "Bad objectData id!");
1118 NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id");
1120 if (aOverwrite) {
1121 nsCOMPtr<mozIStorageStatement> deleteStmt =
1122 aTransaction->GetCachedStatement(
1123 "DELETE FROM unique_index_data "
1124 "WHERE object_data_id = :object_data_id; "
1125 "DELETE FROM index_data "
1126 "WHERE object_data_id = :object_data_id");
1127 NS_ENSURE_TRUE(deleteStmt, NS_ERROR_FAILURE);
1129 mozStorageStatementScoper scoper(deleteStmt);
1131 rv = deleteStmt->BindInt64ByName(objectDataId, aObjectDataId);
1132 NS_ENSURE_SUCCESS(rv, rv);
1134 rv = deleteStmt->Execute();
1135 NS_ENSURE_SUCCESS(rv, rv);
1138 // Avoid lots of hash lookups for objectStores with lots of indexes by lazily
1139 // holding the necessary statements on the stack outside the loop.
1140 nsCOMPtr<mozIStorageStatement> insertUniqueStmt;
1141 nsCOMPtr<mozIStorageStatement> insertStmt;
1143 uint32_t infoCount = aUpdateInfoArray.Length();
1144 for (uint32_t i = 0; i < infoCount; i++) {
1145 const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i];
1147 nsCOMPtr<mozIStorageStatement>& stmt =
1148 updateInfo.indexUnique ? insertUniqueStmt : insertStmt;
1150 if (!stmt) {
1151 stmt = updateInfo.indexUnique ?
1152 aTransaction->GetCachedStatement(
1153 "INSERT INTO unique_index_data "
1154 "(index_id, object_data_id, object_data_key, value) "
1155 "VALUES (:index_id, :object_data_id, :object_data_key, :value)") :
1156 aTransaction->GetCachedStatement(
1157 "INSERT OR IGNORE INTO index_data ("
1158 "index_id, object_data_id, object_data_key, value) "
1159 "VALUES (:index_id, :object_data_id, :object_data_key, :value)");
1161 NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE);
1163 mozStorageStatementScoper scoper(stmt);
1165 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
1166 updateInfo.indexId);
1167 NS_ENSURE_SUCCESS(rv, rv);
1169 rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
1170 NS_ENSURE_SUCCESS(rv, rv);
1172 rv = aObjectStoreKey.BindToStatement(stmt,
1173 NS_LITERAL_CSTRING("object_data_key"));
1174 NS_ENSURE_SUCCESS(rv, rv);
1176 rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value"));
1177 NS_ENSURE_SUCCESS(rv, rv);
1179 rv = stmt->Execute();
1180 if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) {
1181 // If we're inserting multiple entries for the same unique index, then
1182 // we might have failed to insert due to colliding with another entry for
1183 // the same index in which case we should ignore it.
1185 for (int32_t j = (int32_t)i - 1;
1186 j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId;
1187 --j) {
1188 if (updateInfo.value == aUpdateInfoArray[j].value) {
1189 // We found a key with the same value for the same index. So we
1190 // must have had a collision with a value we just inserted.
1191 rv = NS_OK;
1192 break;
1197 if (NS_FAILED(rv)) {
1198 return rv;
1202 return NS_OK;
1205 // static
1206 nsresult
1207 IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
1208 mozIStorageStatement* aStatement,
1209 uint32_t aDataIndex,
1210 uint32_t aFileIdsIndex,
1211 IDBDatabase* aDatabase,
1212 StructuredCloneReadInfo& aInfo)
1214 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
1215 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1217 PROFILER_LABEL("IDBObjectStore", "GetStructuredCloneReadInfoFromStatement",
1218 js::ProfileEntry::Category::STORAGE);
1220 #ifdef DEBUG
1222 int32_t type;
1223 NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) &&
1224 type == mozIStorageStatement::VALUE_TYPE_BLOB,
1225 "Bad value type!");
1227 #endif
1229 const uint8_t* blobData;
1230 uint32_t blobDataLength;
1231 nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength,
1232 &blobData);
1233 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1235 const char* compressed = reinterpret_cast<const char*>(blobData);
1236 size_t compressedLength = size_t(blobDataLength);
1238 static const fallible_t fallible = fallible_t();
1240 size_t uncompressedLength;
1241 if (!snappy::GetUncompressedLength(compressed, compressedLength,
1242 &uncompressedLength)) {
1243 IDB_WARNING("Snappy can't determine uncompressed length!");
1244 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1247 nsAutoArrayPtr<char> uncompressed(new (fallible) char[uncompressedLength]);
1248 NS_ENSURE_TRUE(uncompressed, NS_ERROR_OUT_OF_MEMORY);
1250 if (!snappy::RawUncompress(compressed, compressedLength,
1251 uncompressed.get())) {
1252 IDB_WARNING("Snappy can't determine uncompressed length!");
1253 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1256 JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer;
1257 if (!buffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
1258 uncompressedLength)) {
1259 IDB_REPORT_INTERNAL_ERR();
1260 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1263 bool isNull;
1264 rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
1265 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1267 if (!isNull) {
1268 nsString ids;
1269 rv = aStatement->GetString(aFileIdsIndex, ids);
1270 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1272 nsAutoTArray<int64_t, 10> array;
1273 rv = ConvertFileIdsToArray(ids, array);
1274 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1276 FileManager* fileManager = aDatabase->Manager();
1278 for (uint32_t i = 0; i < array.Length(); i++) {
1279 const int64_t& id = array[i];
1281 nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(id);
1282 NS_ASSERTION(fileInfo, "Null file info!");
1284 StructuredCloneFile* file = aInfo.mFiles.AppendElement();
1285 file->mFileInfo.swap(fileInfo);
1289 aInfo.mDatabase = aDatabase;
1291 return NS_OK;
1294 // static
1295 void
1296 IDBObjectStore::ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo)
1298 // This is kind of tricky, we only want to release stuff on the main thread,
1299 // but we can end up being called on other threads if we have already been
1300 // cleared on the main thread.
1301 if (!aWriteInfo.mCloneBuffer.data() && !aWriteInfo.mFiles.Length()) {
1302 return;
1305 // If there's something to clear, we should be on the main thread.
1306 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1308 ClearStructuredCloneBuffer(aWriteInfo.mCloneBuffer);
1309 aWriteInfo.mFiles.Clear();
1312 // static
1313 void
1314 IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
1316 // This is kind of tricky, we only want to release stuff on the main thread,
1317 // but we can end up being called on other threads if we have already been
1318 // cleared on the main thread.
1319 if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
1320 return;
1323 // If there's something to clear, we should be on the main thread.
1324 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1326 ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer);
1327 aReadInfo.mFiles.Clear();
1330 // static
1331 void
1332 IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
1334 if (aBuffer.data()) {
1335 aBuffer.clear();
1339 // static
1340 bool
1341 IDBObjectStore::DeserializeValue(JSContext* aCx,
1342 StructuredCloneReadInfo& aCloneReadInfo,
1343 JS::MutableHandle<JS::Value> aValue)
1345 NS_ASSERTION(NS_IsMainThread(),
1346 "Should only be deserializing on the main thread!");
1347 NS_ASSERTION(aCx, "A JSContext is required!");
1349 JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer;
1351 if (!buffer.data()) {
1352 aValue.setUndefined();
1353 return true;
1356 JSAutoRequest ar(aCx);
1358 JSStructuredCloneCallbacks callbacks = {
1359 IDBObjectStore::StructuredCloneReadCallback<MainThreadDeserializationTraits>,
1360 nullptr,
1361 nullptr,
1362 nullptr,
1363 nullptr,
1364 nullptr
1367 return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
1370 // static
1371 bool
1372 IDBObjectStore::SerializeValue(JSContext* aCx,
1373 StructuredCloneWriteInfo& aCloneWriteInfo,
1374 JS::Handle<JS::Value> aValue)
1376 NS_ASSERTION(NS_IsMainThread(),
1377 "Should only be serializing on the main thread!");
1378 NS_ASSERTION(aCx, "A JSContext is required!");
1380 JSAutoRequest ar(aCx);
1382 JSStructuredCloneCallbacks callbacks = {
1383 nullptr,
1384 StructuredCloneWriteCallback,
1385 nullptr,
1386 nullptr,
1387 nullptr,
1388 nullptr
1391 JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer;
1393 return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo);
1396 static inline bool
1397 StructuredCloneReadString(JSStructuredCloneReader* aReader,
1398 nsCString& aString)
1400 uint32_t length;
1401 if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
1402 NS_WARNING("Failed to read length!");
1403 return false;
1405 length = NativeEndian::swapFromLittleEndian(length);
1407 if (!aString.SetLength(length, fallible_t())) {
1408 NS_WARNING("Out of memory?");
1409 return false;
1411 char* buffer = aString.BeginWriting();
1413 if (!JS_ReadBytes(aReader, buffer, length)) {
1414 NS_WARNING("Failed to read type!");
1415 return false;
1418 return true;
1421 // static
1422 bool
1423 IDBObjectStore::ReadMutableFile(JSStructuredCloneReader* aReader,
1424 MutableFileData* aRetval)
1426 static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004,
1427 "Update me!");
1428 MOZ_ASSERT(aReader && aRetval);
1430 nsCString type;
1431 if (!StructuredCloneReadString(aReader, type)) {
1432 return false;
1434 CopyUTF8toUTF16(type, aRetval->type);
1436 nsCString name;
1437 if (!StructuredCloneReadString(aReader, name)) {
1438 return false;
1440 CopyUTF8toUTF16(name, aRetval->name);
1442 return true;
1445 // static
1446 bool
1447 IDBObjectStore::ReadBlobOrFile(JSStructuredCloneReader* aReader,
1448 uint32_t aTag,
1449 BlobOrFileData* aRetval)
1451 static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 &&
1452 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 &&
1453 SCTAG_DOM_FILE == 0xFFFF8005,
1454 "Update me!");
1455 MOZ_ASSERT(aReader && aRetval);
1456 MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
1457 aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
1458 aTag == SCTAG_DOM_BLOB);
1460 aRetval->tag = aTag;
1462 // If it's not a MutableFile, it's a Blob or a File.
1463 uint64_t size;
1464 if (!JS_ReadBytes(aReader, &size, sizeof(uint64_t))) {
1465 NS_WARNING("Failed to read size!");
1466 return false;
1468 aRetval->size = NativeEndian::swapFromLittleEndian(size);
1470 nsCString type;
1471 if (!StructuredCloneReadString(aReader, type)) {
1472 return false;
1474 CopyUTF8toUTF16(type, aRetval->type);
1476 // Blobs are done.
1477 if (aTag == SCTAG_DOM_BLOB) {
1478 return true;
1481 NS_ASSERTION(aTag == SCTAG_DOM_FILE ||
1482 aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE, "Huh?!");
1484 uint64_t lastModifiedDate;
1485 if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
1486 lastModifiedDate = UINT64_MAX;
1488 else {
1489 if(!JS_ReadBytes(aReader, &lastModifiedDate, sizeof(lastModifiedDate))) {
1490 NS_WARNING("Failed to read lastModifiedDate");
1491 return false;
1493 lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
1495 aRetval->lastModifiedDate = lastModifiedDate;
1497 nsCString name;
1498 if (!StructuredCloneReadString(aReader, name)) {
1499 return false;
1501 CopyUTF8toUTF16(name, aRetval->name);
1503 return true;
1506 // static
1507 template <class Traits>
1508 JSObject*
1509 IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
1510 JSStructuredCloneReader* aReader,
1511 uint32_t aTag,
1512 uint32_t aData,
1513 void* aClosure)
1515 // We need to statically assert that our tag values are what we expect
1516 // so that if people accidentally change them they notice.
1517 static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 &&
1518 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 &&
1519 SCTAG_DOM_MUTABLEFILE == 0xFFFF8004 &&
1520 SCTAG_DOM_FILE == 0xFFFF8005,
1521 "You changed our structured clone tag values and just ate "
1522 "everyone's IndexedDB data. I hope you are happy.");
1524 if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
1525 aTag == SCTAG_DOM_MUTABLEFILE ||
1526 aTag == SCTAG_DOM_BLOB ||
1527 aTag == SCTAG_DOM_FILE) {
1528 StructuredCloneReadInfo* cloneReadInfo =
1529 reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
1531 if (aData >= cloneReadInfo->mFiles.Length()) {
1532 NS_ERROR("Bad blob index!");
1533 return nullptr;
1536 StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
1537 IDBDatabase* database = cloneReadInfo->mDatabase;
1539 if (aTag == SCTAG_DOM_MUTABLEFILE) {
1540 MutableFileData data;
1541 if (!ReadMutableFile(aReader, &data)) {
1542 return nullptr;
1545 JS::Rooted<JSObject*> result(aCx);
1546 if (NS_WARN_IF(!Traits::CreateAndWrapMutableFile(aCx,
1547 database,
1548 file,
1549 data,
1550 &result))) {
1551 return nullptr;
1554 return result;
1557 BlobOrFileData data;
1558 if (!ReadBlobOrFile(aReader, aTag, &data)) {
1559 return nullptr;
1562 return Traits::CreateAndWrapBlobOrFile(aCx, database, file, data);
1565 const JSStructuredCloneCallbacks* runtimeCallbacks =
1566 js::GetContextStructuredCloneCallbacks(aCx);
1568 if (runtimeCallbacks) {
1569 return runtimeCallbacks->read(aCx, aReader, aTag, aData, nullptr);
1572 return nullptr;
1575 // static
1576 bool
1577 IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
1578 JSStructuredCloneWriter* aWriter,
1579 JS::Handle<JSObject*> aObj,
1580 void* aClosure)
1582 StructuredCloneWriteInfo* cloneWriteInfo =
1583 reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
1585 if (JS_GetClass(aObj) == &sDummyPropJSClass) {
1586 NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0,
1587 "We should not have been here before!");
1588 cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter);
1590 uint64_t value = 0;
1591 // Omit endian swap
1592 return JS_WriteBytes(aWriter, &value, sizeof(value));
1595 IDBTransaction* transaction = cloneWriteInfo->mTransaction;
1596 FileManager* fileManager = transaction->Database()->Manager();
1598 IDBMutableFile* mutableFile = nullptr;
1599 if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) {
1600 nsRefPtr<FileInfo> fileInfo = mutableFile->GetFileInfo();
1601 MOZ_ASSERT(fileInfo);
1603 // Throw when trying to store mutable files across databases.
1604 if (fileInfo->Manager() != fileManager) {
1605 return false;
1608 NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
1609 uint32_t convTypeLength =
1610 NativeEndian::swapToLittleEndian(convType.Length());
1612 NS_ConvertUTF16toUTF8 convName(mutableFile->Name());
1613 uint32_t convNameLength =
1614 NativeEndian::swapToLittleEndian(convName.Length());
1616 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE,
1617 cloneWriteInfo->mFiles.Length()) ||
1618 !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
1619 !JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
1620 !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
1621 !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
1622 return false;
1625 StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement();
1626 file->mFileInfo = fileInfo.forget();
1628 return true;
1631 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
1632 nsContentUtils::XPConnect()->
1633 GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
1635 if (wrappedNative) {
1636 nsISupports* supports = wrappedNative->Native();
1638 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
1639 if (blob) {
1640 nsCOMPtr<nsIInputStream> inputStream;
1642 // Check if it is a blob created from this db or the blob was already
1643 // stored in this db
1644 nsRefPtr<FileInfo> fileInfo = transaction->GetFileInfo(blob);
1645 if (!fileInfo && fileManager) {
1646 fileInfo = blob->GetFileInfo(fileManager);
1648 if (!fileInfo) {
1649 fileInfo = fileManager->GetNewFileInfo();
1650 if (!fileInfo) {
1651 NS_WARNING("Failed to get new file info!");
1652 return false;
1655 if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
1656 NS_WARNING("Failed to get internal steam!");
1657 return false;
1660 transaction->AddFileInfo(blob, fileInfo);
1664 uint64_t size;
1665 if (NS_FAILED(blob->GetSize(&size))) {
1666 NS_WARNING("Failed to get size!");
1667 return false;
1669 size = NativeEndian::swapToLittleEndian(size);
1671 nsString type;
1672 if (NS_FAILED(blob->GetType(type))) {
1673 NS_WARNING("Failed to get type!");
1674 return false;
1676 NS_ConvertUTF16toUTF8 convType(type);
1677 uint32_t convTypeLength =
1678 NativeEndian::swapToLittleEndian(convType.Length());
1680 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
1682 if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
1683 cloneWriteInfo->mFiles.Length()) ||
1684 !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
1685 !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
1686 !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
1687 return false;
1690 if (file) {
1691 uint64_t lastModifiedDate = 0;
1692 if (NS_FAILED(file->GetMozLastModifiedDate(&lastModifiedDate))) {
1693 NS_WARNING("Failed to get last modified date!");
1694 return false;
1697 lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate);
1699 nsString name;
1700 if (NS_FAILED(file->GetName(name))) {
1701 NS_WARNING("Failed to get name!");
1702 return false;
1704 NS_ConvertUTF16toUTF8 convName(name);
1705 uint32_t convNameLength =
1706 NativeEndian::swapToLittleEndian(convName.Length());
1708 if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
1709 !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
1710 !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
1711 return false;
1715 StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement();
1716 cloneFile->mFile = blob.forget();
1717 cloneFile->mFileInfo = fileInfo.forget();
1718 cloneFile->mInputStream = inputStream.forget();
1720 return true;
1724 // try using the runtime callbacks
1725 const JSStructuredCloneCallbacks* runtimeCallbacks =
1726 js::GetContextStructuredCloneCallbacks(aCx);
1727 if (runtimeCallbacks) {
1728 return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr);
1731 return false;
1734 // static
1735 nsresult
1736 IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
1737 nsTArray<int64_t>& aResult)
1739 nsCharSeparatedTokenizerTemplate<IgnoreNothing> tokenizer(aFileIds, ' ');
1741 while (tokenizer.hasMoreTokens()) {
1742 nsString token(tokenizer.nextToken());
1744 NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!");
1746 nsresult rv;
1747 int32_t id = token.ToInteger(&rv);
1748 NS_ENSURE_SUCCESS(rv, rv);
1750 int64_t* element = aResult.AppendElement();
1751 *element = id;
1754 return NS_OK;
1757 // static
1758 void
1759 IDBObjectStore::ConvertActorsToBlobs(
1760 const InfallibleTArray<PBlobChild*>& aActors,
1761 nsTArray<StructuredCloneFile>& aFiles)
1763 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1764 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1765 NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!");
1767 if (!aActors.IsEmpty()) {
1768 NS_ASSERTION(ContentChild::GetSingleton(), "This should never be null!");
1770 uint32_t length = aActors.Length();
1771 aFiles.SetCapacity(length);
1773 for (uint32_t index = 0; index < length; index++) {
1774 BlobChild* actor = static_cast<BlobChild*>(aActors[index]);
1776 StructuredCloneFile* file = aFiles.AppendElement();
1777 file->mFile = actor->GetBlob();
1782 // static
1783 nsresult
1784 IDBObjectStore::ConvertBlobsToActors(
1785 nsIContentParent* aContentParent,
1786 FileManager* aFileManager,
1787 const nsTArray<StructuredCloneFile>& aFiles,
1788 InfallibleTArray<PBlobParent*>& aActors)
1790 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1791 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1792 NS_ASSERTION(aContentParent, "Null contentParent!");
1793 NS_ASSERTION(aFileManager, "Null file manager!");
1795 if (!aFiles.IsEmpty()) {
1796 nsCOMPtr<nsIFile> directory = aFileManager->GetDirectory();
1797 if (!directory) {
1798 IDB_WARNING("Failed to get directory!");
1799 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1802 uint32_t fileCount = aFiles.Length();
1803 aActors.SetCapacity(fileCount);
1805 for (uint32_t index = 0; index < fileCount; index++) {
1806 const StructuredCloneFile& file = aFiles[index];
1807 NS_ASSERTION(file.mFileInfo, "This should never be null!");
1809 nsCOMPtr<nsIFile> nativeFile =
1810 aFileManager->GetFileForId(directory, file.mFileInfo->Id());
1811 if (!nativeFile) {
1812 IDB_WARNING("Failed to get file!");
1813 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1816 nsCOMPtr<nsIDOMBlob> blob = DOMFile::CreateFromFile(nativeFile,
1817 file.mFileInfo);
1819 BlobParent* actor =
1820 aContentParent->GetOrCreateActorForBlob(blob);
1821 if (!actor) {
1822 // This can only fail if the child has crashed.
1823 IDB_REPORT_INTERNAL_ERR();
1824 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1827 aActors.AppendElement(actor);
1831 return NS_OK;
1834 IDBObjectStore::IDBObjectStore()
1835 : mId(INT64_MIN),
1836 mKeyPath(0),
1837 mCachedKeyPath(JSVAL_VOID),
1838 mRooted(false),
1839 mAutoIncrement(false),
1840 mActorChild(nullptr),
1841 mActorParent(nullptr)
1843 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1845 SetIsDOMBinding();
1848 IDBObjectStore::~IDBObjectStore()
1850 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1851 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
1852 if (mActorChild) {
1853 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1854 mActorChild->Send__delete__(mActorChild);
1855 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
1858 if (mRooted) {
1859 mCachedKeyPath = JSVAL_VOID;
1860 mozilla::DropJSObjects(this);
1864 nsresult
1865 IDBObjectStore::GetAddInfo(JSContext* aCx,
1866 JS::Handle<JS::Value> aValue,
1867 JS::Handle<JS::Value> aKeyVal,
1868 StructuredCloneWriteInfo& aCloneWriteInfo,
1869 Key& aKey,
1870 nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1872 nsresult rv;
1874 // Return DATA_ERR if a key was passed in and this objectStore uses inline
1875 // keys.
1876 if (!aKeyVal.isUndefined() && HasValidKeyPath()) {
1877 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1880 JSAutoRequest ar(aCx);
1882 if (!HasValidKeyPath()) {
1883 // Out-of-line keys must be passed in.
1884 rv = aKey.SetFromJSVal(aCx, aKeyVal);
1885 if (NS_FAILED(rv)) {
1886 return rv;
1889 else if (!mAutoIncrement) {
1890 rv = GetKeyPath().ExtractKey(aCx, aValue, aKey);
1891 if (NS_FAILED(rv)) {
1892 return rv;
1896 // Return DATA_ERR if no key was specified this isn't an autoIncrement
1897 // objectStore.
1898 if (aKey.IsUnset() && !mAutoIncrement) {
1899 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1902 // Figure out indexes and the index values to update here.
1903 uint32_t count = mInfo->indexes.Length();
1904 aUpdateInfoArray.SetCapacity(count); // Pretty good estimate
1905 for (uint32_t indexesIndex = 0; indexesIndex < count; indexesIndex++) {
1906 const IndexInfo& indexInfo = mInfo->indexes[indexesIndex];
1908 rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath,
1909 indexInfo.unique, indexInfo.multiEntry, aCx,
1910 aValue, aUpdateInfoArray);
1911 NS_ENSURE_SUCCESS(rv, rv);
1914 GetAddInfoClosure data = {this, aCloneWriteInfo, aValue};
1916 if (mAutoIncrement && HasValidKeyPath()) {
1917 NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!");
1919 rv = GetKeyPath().ExtractOrCreateKey(aCx, aValue, aKey,
1920 &GetAddInfoCallback, &data);
1922 else {
1923 rv = GetAddInfoCallback(aCx, &data);
1926 return rv;
1929 already_AddRefed<IDBRequest>
1930 IDBObjectStore::AddOrPut(JSContext* aCx, JS::Handle<JS::Value> aValue,
1931 JS::Handle<JS::Value> aKey,
1932 bool aOverwrite, ErrorResult& aRv)
1934 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1936 if (!mTransaction->IsOpen()) {
1937 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1938 return nullptr;
1941 if (!IsWriteAllowed()) {
1942 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1943 return nullptr;
1946 StructuredCloneWriteInfo cloneWriteInfo;
1947 Key key;
1948 nsTArray<IndexUpdateInfo> updateInfo;
1950 JS::Rooted<JS::Value> value(aCx, aValue);
1951 aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo);
1952 if (aRv.Failed()) {
1953 return nullptr;
1956 nsRefPtr<IDBRequest> request = GenerateRequest(this);
1957 if (!request) {
1958 IDB_WARNING("Failed to generate request!");
1959 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1960 return nullptr;
1963 nsRefPtr<AddHelper> helper =
1964 new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key,
1965 aOverwrite, updateInfo);
1967 nsresult rv = helper->DispatchToTransactionPool();
1968 if (NS_FAILED(rv)) {
1969 IDB_WARNING("Failed to dispatch!");
1970 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1971 return nullptr;
1974 #ifdef IDB_PROFILER_USE_MARKS
1975 if (aOverwrite) {
1976 IDB_PROFILER_MARK("IndexedDB Request %llu: "
1977 "database(%s).transaction(%s).objectStore(%s).%s(%s)",
1978 "IDBRequest[%llu] MT IDBObjectStore.put()",
1979 request->GetSerialNumber(),
1980 IDB_PROFILER_STRING(Transaction()->Database()),
1981 IDB_PROFILER_STRING(Transaction()),
1982 IDB_PROFILER_STRING(this),
1983 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
1985 else {
1986 IDB_PROFILER_MARK("IndexedDB Request %llu: "
1987 "database(%s).transaction(%s).objectStore(%s).add(%s)",
1988 "IDBRequest[%llu] MT IDBObjectStore.add()",
1989 request->GetSerialNumber(),
1990 IDB_PROFILER_STRING(Transaction()->Database()),
1991 IDB_PROFILER_STRING(Transaction()),
1992 IDB_PROFILER_STRING(this),
1993 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
1995 #endif
1997 return request.forget();
2000 nsresult
2001 IDBObjectStore::AddOrPutInternal(
2002 const SerializedStructuredCloneWriteInfo& aCloneWriteInfo,
2003 const Key& aKey,
2004 const InfallibleTArray<IndexUpdateInfo>& aUpdateInfoArray,
2005 const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
2006 bool aOverwrite,
2007 IDBRequest** _retval)
2009 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2010 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
2012 if (!mTransaction->IsOpen()) {
2013 return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
2016 if (!IsWriteAllowed()) {
2017 return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
2020 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2021 IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2023 StructuredCloneWriteInfo cloneWriteInfo;
2024 if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) {
2025 IDB_WARNING("Failed to copy structured clone buffer!");
2026 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2029 if (!aBlobs.IsEmpty()) {
2030 FileManager* fileManager = Transaction()->Database()->Manager();
2031 NS_ASSERTION(fileManager, "Null file manager?!");
2033 uint32_t length = aBlobs.Length();
2034 cloneWriteInfo.mFiles.SetCapacity(length);
2036 for (uint32_t index = 0; index < length; index++) {
2037 const nsCOMPtr<nsIDOMBlob>& blob = aBlobs[index];
2039 nsCOMPtr<nsIInputStream> inputStream;
2041 nsRefPtr<FileInfo> fileInfo = Transaction()->GetFileInfo(blob);
2042 if (!fileInfo) {
2043 fileInfo = blob->GetFileInfo(fileManager);
2045 if (!fileInfo) {
2046 fileInfo = fileManager->GetNewFileInfo();
2047 if (!fileInfo) {
2048 IDB_WARNING("Failed to get new file info!");
2049 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2052 if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
2053 IDB_WARNING("Failed to get internal steam!");
2054 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2057 // XXXbent This is where we should send a message back to the child to
2058 // update the file id.
2060 Transaction()->AddFileInfo(blob, fileInfo);
2064 StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement();
2065 file->mFile = blob;
2066 file->mFileInfo.swap(fileInfo);
2067 file->mInputStream.swap(inputStream);
2071 Key key(aKey);
2073 nsTArray<IndexUpdateInfo> updateInfo(aUpdateInfoArray);
2075 nsRefPtr<AddHelper> helper =
2076 new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key,
2077 aOverwrite, updateInfo);
2079 nsresult rv = helper->DispatchToTransactionPool();
2080 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2082 #ifdef IDB_PROFILER_USE_MARKS
2083 if (aOverwrite) {
2084 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2085 "database(%s).transaction(%s).objectStore(%s).%s(%s)",
2086 "IDBRequest[%llu] MT IDBObjectStore.put()",
2087 request->GetSerialNumber(),
2088 IDB_PROFILER_STRING(Transaction()->Database()),
2089 IDB_PROFILER_STRING(Transaction()),
2090 IDB_PROFILER_STRING(this),
2091 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
2093 else {
2094 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2095 "database(%s).transaction(%s).objectStore(%s).add(%s)",
2096 "IDBRequest[%llu] MT IDBObjectStore.add()",
2097 request->GetSerialNumber(),
2098 IDB_PROFILER_STRING(Transaction()->Database()),
2099 IDB_PROFILER_STRING(Transaction()),
2100 IDB_PROFILER_STRING(this),
2101 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
2103 #endif
2105 request.forget(_retval);
2106 return NS_OK;
2109 already_AddRefed<IDBRequest>
2110 IDBObjectStore::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv)
2112 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2113 NS_ASSERTION(aKeyRange, "Null pointer!");
2115 if (!mTransaction->IsOpen()) {
2116 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2117 return nullptr;
2120 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2121 if (!request) {
2122 IDB_WARNING("Failed to generate request!");
2123 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2124 return nullptr;
2127 nsRefPtr<GetHelper> helper =
2128 new GetHelper(mTransaction, request, this, aKeyRange);
2130 nsresult rv = helper->DispatchToTransactionPool();
2131 if (NS_FAILED(rv)) {
2132 IDB_WARNING("Failed to dispatch!");
2133 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2134 return nullptr;
2137 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2138 "database(%s).transaction(%s).objectStore(%s).get(%s)",
2139 "IDBRequest[%llu] MT IDBObjectStore.get()",
2140 request->GetSerialNumber(),
2141 IDB_PROFILER_STRING(Transaction()->Database()),
2142 IDB_PROFILER_STRING(Transaction()),
2143 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
2145 return request.forget();
2148 already_AddRefed<IDBRequest>
2149 IDBObjectStore::GetAllInternal(IDBKeyRange* aKeyRange,
2150 uint32_t aLimit, ErrorResult& aRv)
2152 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2154 if (!mTransaction->IsOpen()) {
2155 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2156 return nullptr;
2159 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2160 if (!request) {
2161 IDB_WARNING("Failed to generate request!");
2162 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2163 return nullptr;
2166 nsRefPtr<GetAllHelper> helper =
2167 new GetAllHelper(mTransaction, request, this, aKeyRange, aLimit);
2169 nsresult rv = helper->DispatchToTransactionPool();
2170 if (NS_FAILED(rv)) {
2171 IDB_WARNING("Failed to dispatch!");
2172 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2173 return nullptr;
2176 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2177 "database(%s).transaction(%s).objectStore(%s)."
2178 "getAll(%s, %lu)",
2179 "IDBRequest[%llu] MT IDBObjectStore.getAll()",
2180 request->GetSerialNumber(),
2181 IDB_PROFILER_STRING(Transaction()->Database()),
2182 IDB_PROFILER_STRING(Transaction()),
2183 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2184 aLimit);
2186 return request.forget();
2189 already_AddRefed<IDBRequest>
2190 IDBObjectStore::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit,
2191 ErrorResult& aRv)
2193 MOZ_ASSERT(NS_IsMainThread());
2195 if (!mTransaction->IsOpen()) {
2196 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2197 return nullptr;
2200 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2201 if (!request) {
2202 IDB_WARNING("Failed to generate request!");
2203 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2204 return nullptr;
2207 nsRefPtr<GetAllKeysHelper> helper =
2208 new GetAllKeysHelper(mTransaction, request, this, aKeyRange, aLimit);
2210 nsresult rv = helper->DispatchToTransactionPool();
2211 if (NS_FAILED(rv)) {
2212 IDB_WARNING("Failed to dispatch!");
2213 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2214 return nullptr;
2217 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2218 "database(%s).transaction(%s).objectStore(%s)."
2219 "getAllKeys(%s, %lu)",
2220 "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()",
2221 request->GetSerialNumber(),
2222 IDB_PROFILER_STRING(Transaction()->Database()),
2223 IDB_PROFILER_STRING(Transaction()),
2224 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2225 aLimit);
2227 return request.forget();
2230 already_AddRefed<IDBRequest>
2231 IDBObjectStore::DeleteInternal(IDBKeyRange* aKeyRange,
2232 ErrorResult& aRv)
2234 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2235 NS_ASSERTION(aKeyRange, "Null key range!");
2237 if (!mTransaction->IsOpen()) {
2238 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2239 return nullptr;
2242 if (!IsWriteAllowed()) {
2243 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2244 return nullptr;
2247 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2248 if (!request) {
2249 IDB_WARNING("Failed to generate request!");
2250 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2251 return nullptr;
2254 nsRefPtr<DeleteHelper> helper =
2255 new DeleteHelper(mTransaction, request, this, aKeyRange);
2257 nsresult rv = helper->DispatchToTransactionPool();
2258 if (NS_FAILED(rv)) {
2259 IDB_WARNING("Failed to dispatch!");
2260 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2261 return nullptr;
2264 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2265 "database(%s).transaction(%s).objectStore(%s).delete(%s)",
2266 "IDBRequest[%llu] MT IDBObjectStore.delete()",
2267 request->GetSerialNumber(),
2268 IDB_PROFILER_STRING(Transaction()->Database()),
2269 IDB_PROFILER_STRING(Transaction()),
2270 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
2272 return request.forget();
2275 already_AddRefed<IDBRequest>
2276 IDBObjectStore::Clear(ErrorResult& aRv)
2278 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2280 if (!mTransaction->IsOpen()) {
2281 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2282 return nullptr;
2285 if (!IsWriteAllowed()) {
2286 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2287 return nullptr;
2290 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2291 if (!request) {
2292 IDB_WARNING("Failed to generate request!");
2293 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2294 return nullptr;
2297 nsRefPtr<ClearHelper> helper(new ClearHelper(mTransaction, request, this));
2299 nsresult rv = helper->DispatchToTransactionPool();
2300 if (NS_FAILED(rv)) {
2301 IDB_WARNING("Failed to dispatch!");
2302 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2303 return nullptr;
2306 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2307 "database(%s).transaction(%s).objectStore(%s).clear()",
2308 "IDBRequest[%llu] MT IDBObjectStore.clear()",
2309 request->GetSerialNumber(),
2310 IDB_PROFILER_STRING(Transaction()->Database()),
2311 IDB_PROFILER_STRING(Transaction()),
2312 IDB_PROFILER_STRING(this));
2314 return request.forget();
2317 already_AddRefed<IDBRequest>
2318 IDBObjectStore::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv)
2320 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2322 if (!mTransaction->IsOpen()) {
2323 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2324 return nullptr;
2327 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2328 if (!request) {
2329 IDB_WARNING("Failed to generate request!");
2330 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2331 return nullptr;
2334 nsRefPtr<CountHelper> helper =
2335 new CountHelper(mTransaction, request, this, aKeyRange);
2336 nsresult rv = helper->DispatchToTransactionPool();
2337 if (NS_FAILED(rv)) {
2338 IDB_WARNING("Failed to dispatch!");
2339 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2340 return nullptr;
2343 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2344 "database(%s).transaction(%s).objectStore(%s).count(%s)",
2345 "IDBRequest[%llu] MT IDBObjectStore.count()",
2346 request->GetSerialNumber(),
2347 IDB_PROFILER_STRING(Transaction()->Database()),
2348 IDB_PROFILER_STRING(Transaction()),
2349 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
2351 return request.forget();
2354 already_AddRefed<IDBRequest>
2355 IDBObjectStore::OpenCursorInternal(IDBKeyRange* aKeyRange,
2356 size_t aDirection, ErrorResult& aRv)
2358 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2360 if (!mTransaction->IsOpen()) {
2361 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2362 return nullptr;
2365 IDBCursor::Direction direction =
2366 static_cast<IDBCursor::Direction>(aDirection);
2368 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2369 if (!request) {
2370 IDB_WARNING("Failed to generate request!");
2371 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2372 return nullptr;
2375 nsRefPtr<OpenCursorHelper> helper =
2376 new OpenCursorHelper(mTransaction, request, this, aKeyRange, direction);
2378 nsresult rv = helper->DispatchToTransactionPool();
2379 if (NS_FAILED(rv)) {
2380 IDB_WARNING("Failed to dispatch!");
2381 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2382 return nullptr;
2385 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2386 "database(%s).transaction(%s).objectStore(%s)."
2387 "openCursor(%s, %s)",
2388 "IDBRequest[%llu] MT IDBObjectStore.openCursor()",
2389 request->GetSerialNumber(),
2390 IDB_PROFILER_STRING(Transaction()->Database()),
2391 IDB_PROFILER_STRING(Transaction()),
2392 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2393 IDB_PROFILER_STRING(direction));
2395 return request.forget();
2398 nsresult
2399 IDBObjectStore::OpenCursorFromChildProcess(
2400 IDBRequest* aRequest,
2401 size_t aDirection,
2402 const Key& aKey,
2403 const SerializedStructuredCloneReadInfo& aCloneInfo,
2404 nsTArray<StructuredCloneFile>& aBlobs,
2405 IDBCursor** _retval)
2407 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2408 NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) ||
2409 (aCloneInfo.dataLength && aCloneInfo.data),
2410 "Inconsistent clone info!");
2412 IDBCursor::Direction direction =
2413 static_cast<IDBCursor::Direction>(aDirection);
2415 StructuredCloneReadInfo cloneInfo;
2417 if (!cloneInfo.SetFromSerialized(aCloneInfo)) {
2418 IDB_WARNING("Failed to copy clone buffer!");
2419 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2422 cloneInfo.mFiles.SwapElements(aBlobs);
2424 nsRefPtr<IDBCursor> cursor =
2425 IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
2426 EmptyCString(), EmptyCString(), aKey, Move(cloneInfo));
2427 IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2429 NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
2431 cursor.forget(_retval);
2432 return NS_OK;
2435 nsresult
2436 IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest,
2437 size_t aDirection,
2438 const Key& aKey,
2439 IDBCursor** _retval)
2441 MOZ_ASSERT(NS_IsMainThread());
2442 MOZ_ASSERT(aRequest);
2444 auto direction = static_cast<IDBCursor::Direction>(aDirection);
2446 nsRefPtr<IDBCursor> cursor =
2447 IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
2448 EmptyCString(), EmptyCString(), aKey);
2449 IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2451 cursor.forget(_retval);
2452 return NS_OK;
2455 already_AddRefed<IDBRequest>
2456 IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection,
2457 ErrorResult& aRv)
2459 MOZ_ASSERT(NS_IsMainThread());
2461 if (!mTransaction->IsOpen()) {
2462 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2463 return nullptr;
2466 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2467 if (!request) {
2468 IDB_WARNING("Failed to generate request!");
2469 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2470 return nullptr;
2473 auto direction = static_cast<IDBCursor::Direction>(aDirection);
2475 nsRefPtr<OpenKeyCursorHelper> helper =
2476 new OpenKeyCursorHelper(mTransaction, request, this, aKeyRange, direction);
2478 nsresult rv = helper->DispatchToTransactionPool();
2479 if (NS_FAILED(rv)) {
2480 IDB_WARNING("Failed to dispatch!");
2481 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2482 return nullptr;
2485 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2486 "database(%s).transaction(%s).objectStore(%s)."
2487 "openKeyCursor(%s, %s)",
2488 "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()",
2489 request->GetSerialNumber(),
2490 IDB_PROFILER_STRING(Transaction()->Database()),
2491 IDB_PROFILER_STRING(Transaction()),
2492 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2493 IDB_PROFILER_STRING(direction));
2495 return request.forget();
2498 void
2499 IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo)
2501 NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
2502 NS_ASSERTION(aInfo != mInfo, "This is nonsense");
2504 mInfo = aInfo;
2507 already_AddRefed<IDBIndex>
2508 IDBObjectStore::CreateIndexInternal(const IndexInfo& aInfo, ErrorResult& aRv)
2510 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2512 IndexInfo* indexInfo = mInfo->indexes.AppendElement();
2514 indexInfo->name = aInfo.name;
2515 indexInfo->id = aInfo.id;
2516 indexInfo->keyPath = aInfo.keyPath;
2517 indexInfo->unique = aInfo.unique;
2518 indexInfo->multiEntry = aInfo.multiEntry;
2520 // Don't leave this in the list if we fail below!
2521 AutoRemoveIndex autoRemove(mInfo, aInfo.name);
2523 nsRefPtr<IDBIndex> index = IDBIndex::Create(this, indexInfo, true);
2525 mCreatedIndexes.AppendElement(index);
2527 if (IndexedDatabaseManager::IsMainProcess()) {
2528 nsRefPtr<CreateIndexHelper> helper =
2529 new CreateIndexHelper(mTransaction, index);
2531 nsresult rv = helper->DispatchToTransactionPool();
2532 if (NS_FAILED(rv)) {
2533 IDB_WARNING("Failed to dispatch!");
2534 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2535 return nullptr;
2539 autoRemove.forget();
2541 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
2542 "database(%s).transaction(%s).objectStore(%s)."
2543 "createIndex(%s)",
2544 "MT IDBObjectStore.createIndex()",
2545 IDB_PROFILER_STRING(Transaction()->Database()),
2546 IDB_PROFILER_STRING(Transaction()),
2547 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index));
2549 return index.forget();
2552 already_AddRefed<IDBIndex>
2553 IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
2555 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2557 if (mTransaction->IsFinished()) {
2558 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2559 return nullptr;
2562 IndexInfo* indexInfo = nullptr;
2563 uint32_t indexCount = mInfo->indexes.Length();
2564 for (uint32_t index = 0; index < indexCount; index++) {
2565 if (mInfo->indexes[index].name == aName) {
2566 indexInfo = &(mInfo->indexes[index]);
2567 break;
2571 if (!indexInfo) {
2572 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
2573 return nullptr;
2576 nsRefPtr<IDBIndex> retval;
2577 for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) {
2578 nsRefPtr<IDBIndex>& index = mCreatedIndexes[i];
2579 if (index->Name() == aName) {
2580 retval = index;
2581 break;
2585 if (!retval) {
2586 retval = IDBIndex::Create(this, indexInfo, false);
2587 if (!retval) {
2588 IDB_WARNING("Failed to create index!");
2589 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2590 return nullptr;
2593 if (!mCreatedIndexes.AppendElement(retval)) {
2594 IDB_WARNING("Out of memory!");
2595 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2596 return nullptr;
2600 return retval.forget();
2603 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
2605 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
2606 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2607 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
2608 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2610 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
2611 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
2612 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
2614 for (uint32_t i = 0; i < tmp->mCreatedIndexes.Length(); i++) {
2615 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]");
2616 cb.NoteXPCOMChild(static_cast<nsISupports*>(tmp->mCreatedIndexes[i].get()));
2618 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2620 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
2621 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
2623 // Don't unlink mTransaction!
2625 tmp->mCreatedIndexes.Clear();
2627 tmp->mCachedKeyPath = JSVAL_VOID;
2629 if (tmp->mRooted) {
2630 mozilla::DropJSObjects(tmp);
2631 tmp->mRooted = false;
2633 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2635 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
2636 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
2637 NS_INTERFACE_MAP_ENTRY(nsISupports)
2638 NS_INTERFACE_MAP_END
2640 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
2641 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
2643 JSObject*
2644 IDBObjectStore::WrapObject(JSContext* aCx)
2646 return IDBObjectStoreBinding::Wrap(aCx, this);
2649 void
2650 IDBObjectStore::GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
2651 ErrorResult& aRv)
2653 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2655 if (!mCachedKeyPath.isUndefined()) {
2656 JS::ExposeValueToActiveJS(mCachedKeyPath);
2657 aResult.set(mCachedKeyPath);
2658 return;
2661 aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
2662 if (NS_WARN_IF(aRv.Failed())) {
2663 return;
2666 if (mCachedKeyPath.isGCThing()) {
2667 mozilla::HoldJSObjects(this);
2668 mRooted = true;
2671 JS::ExposeValueToActiveJS(mCachedKeyPath);
2672 aResult.set(mCachedKeyPath);
2675 already_AddRefed<DOMStringList>
2676 IDBObjectStore::GetIndexNames(ErrorResult& aRv)
2678 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2680 nsRefPtr<DOMStringList> list(new DOMStringList());
2682 nsTArray<nsString>& names = list->StringArray();
2683 uint32_t count = mInfo->indexes.Length();
2684 names.SetCapacity(count);
2686 for (uint32_t index = 0; index < count; index++) {
2687 names.InsertElementSorted(mInfo->indexes[index].name);
2690 return list.forget();
2693 already_AddRefed<IDBRequest>
2694 IDBObjectStore::Get(JSContext* aCx, JS::Handle<JS::Value> aKey,
2695 ErrorResult& aRv)
2697 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2699 if (!mTransaction->IsOpen()) {
2700 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2701 return nullptr;
2704 nsRefPtr<IDBKeyRange> keyRange;
2705 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2706 ENSURE_SUCCESS(aRv, nullptr);
2708 if (!keyRange) {
2709 // Must specify a key or keyRange for get().
2710 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
2711 return nullptr;
2714 return GetInternal(keyRange, aRv);
2717 already_AddRefed<IDBRequest>
2718 IDBObjectStore::GetAll(JSContext* aCx,
2719 JS::Handle<JS::Value> aKey,
2720 const Optional<uint32_t>& aLimit, ErrorResult& aRv)
2722 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2724 if (!mTransaction->IsOpen()) {
2725 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2726 return nullptr;
2729 nsRefPtr<IDBKeyRange> keyRange;
2730 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2731 ENSURE_SUCCESS(aRv, nullptr);
2733 uint32_t limit = UINT32_MAX;
2734 if (aLimit.WasPassed() && aLimit.Value() != 0) {
2735 limit = aLimit.Value();
2738 return GetAllInternal(keyRange, limit, aRv);
2741 already_AddRefed<IDBRequest>
2742 IDBObjectStore::Delete(JSContext* aCx, JS::Handle<JS::Value> aKey,
2743 ErrorResult& aRv)
2745 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2747 if (!mTransaction->IsOpen()) {
2748 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2749 return nullptr;
2752 if (!IsWriteAllowed()) {
2753 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2754 return nullptr;
2757 nsRefPtr<IDBKeyRange> keyRange;
2758 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2759 ENSURE_SUCCESS(aRv, nullptr);
2761 if (!keyRange) {
2762 // Must specify a key or keyRange for delete().
2763 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
2764 return nullptr;
2767 return DeleteInternal(keyRange, aRv);
2770 already_AddRefed<IDBRequest>
2771 IDBObjectStore::OpenCursor(JSContext* aCx,
2772 JS::Handle<JS::Value> aRange,
2773 IDBCursorDirection aDirection, ErrorResult& aRv)
2775 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2777 if (!mTransaction->IsOpen()) {
2778 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2779 return nullptr;
2782 nsRefPtr<IDBKeyRange> keyRange;
2783 aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
2784 ENSURE_SUCCESS(aRv, nullptr);
2786 IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
2787 size_t argDirection = static_cast<size_t>(direction);
2789 return OpenCursorInternal(keyRange, argDirection, aRv);
2792 already_AddRefed<IDBIndex>
2793 IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName,
2794 const nsAString& aKeyPath,
2795 const IDBIndexParameters& aOptionalParameters,
2796 ErrorResult& aRv)
2798 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2800 KeyPath keyPath(0);
2801 if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) ||
2802 !keyPath.IsValid()) {
2803 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2804 return nullptr;
2807 return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv);
2810 already_AddRefed<IDBIndex>
2811 IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName,
2812 const Sequence<nsString >& aKeyPath,
2813 const IDBIndexParameters& aOptionalParameters,
2814 ErrorResult& aRv)
2816 NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
2818 if (!aKeyPath.Length()) {
2819 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2820 return nullptr;
2823 KeyPath keyPath(0);
2824 if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath))) {
2825 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2826 return nullptr;
2829 return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv);
2832 already_AddRefed<IDBIndex>
2833 IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName,
2834 KeyPath& aKeyPath,
2835 const IDBIndexParameters& aOptionalParameters,
2836 ErrorResult& aRv)
2838 // Check name and current mode
2839 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
2841 if (!transaction ||
2842 transaction != mTransaction ||
2843 mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
2844 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2845 return nullptr;
2848 bool found = false;
2849 uint32_t indexCount = mInfo->indexes.Length();
2850 for (uint32_t index = 0; index < indexCount; index++) {
2851 if (mInfo->indexes[index].name == aName) {
2852 found = true;
2853 break;
2857 if (found) {
2858 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
2859 return nullptr;
2862 NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
2864 #ifdef DEBUG
2865 for (uint32_t index = 0; index < mCreatedIndexes.Length(); index++) {
2866 if (mCreatedIndexes[index]->Name() == aName) {
2867 NS_ERROR("Already created this one!");
2870 #endif
2872 if (aOptionalParameters.mMultiEntry && aKeyPath.IsArray()) {
2873 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2874 return nullptr;
2877 DatabaseInfo* databaseInfo = mTransaction->DBInfo();
2879 IndexInfo info;
2881 info.name = aName;
2882 info.id = databaseInfo->nextIndexId++;
2883 info.keyPath = aKeyPath;
2884 info.unique = aOptionalParameters.mUnique;
2885 info.multiEntry = aOptionalParameters.mMultiEntry;
2887 return CreateIndexInternal(info, aRv);
2890 void
2891 IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
2893 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2895 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
2897 if (!transaction ||
2898 transaction != mTransaction ||
2899 mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
2900 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2901 return;
2904 NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
2906 uint32_t index = 0;
2907 for (; index < mInfo->indexes.Length(); index++) {
2908 if (mInfo->indexes[index].name == aName) {
2909 break;
2913 if (index == mInfo->indexes.Length()) {
2914 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
2915 return;
2918 if (IndexedDatabaseManager::IsMainProcess()) {
2919 nsRefPtr<DeleteIndexHelper> helper =
2920 new DeleteIndexHelper(mTransaction, this, aName);
2922 nsresult rv = helper->DispatchToTransactionPool();
2923 if (NS_FAILED(rv)) {
2924 IDB_WARNING("Failed to dispatch!");
2925 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2926 return;
2929 else {
2930 NS_ASSERTION(mActorChild, "Must have an actor here!");
2932 mActorChild->SendDeleteIndex(nsString(aName));
2935 mInfo->indexes.RemoveElementAt(index);
2937 for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) {
2938 if (mCreatedIndexes[i]->Name() == aName) {
2939 mCreatedIndexes.RemoveElementAt(i);
2940 break;
2944 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
2945 "database(%s).transaction(%s).objectStore(%s)."
2946 "deleteIndex(\"%s\")",
2947 "MT IDBObjectStore.deleteIndex()",
2948 IDB_PROFILER_STRING(Transaction()->Database()),
2949 IDB_PROFILER_STRING(Transaction()),
2950 IDB_PROFILER_STRING(this),
2951 NS_ConvertUTF16toUTF8(aName).get());
2954 already_AddRefed<IDBRequest>
2955 IDBObjectStore::Count(JSContext* aCx,
2956 JS::Handle<JS::Value> aKey,
2957 ErrorResult& aRv)
2959 if (!mTransaction->IsOpen()) {
2960 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2961 return nullptr;
2964 nsRefPtr<IDBKeyRange> keyRange;
2965 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2966 ENSURE_SUCCESS(aRv, nullptr);
2968 return CountInternal(keyRange, aRv);
2971 already_AddRefed<IDBRequest>
2972 IDBObjectStore::GetAllKeys(JSContext* aCx,
2973 JS::Handle<JS::Value> aKey,
2974 const Optional<uint32_t>& aLimit, ErrorResult& aRv)
2976 MOZ_ASSERT(NS_IsMainThread());
2978 if (!mTransaction->IsOpen()) {
2979 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2980 return nullptr;
2983 nsRefPtr<IDBKeyRange> keyRange;
2984 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2985 ENSURE_SUCCESS(aRv, nullptr);
2987 uint32_t limit = UINT32_MAX;
2988 if (aLimit.WasPassed() && aLimit.Value() != 0) {
2989 limit = aLimit.Value();
2992 return GetAllKeysInternal(keyRange, limit, aRv);
2995 already_AddRefed<IDBRequest>
2996 IDBObjectStore::OpenKeyCursor(JSContext* aCx,
2997 JS::Handle<JS::Value> aRange,
2998 IDBCursorDirection aDirection, ErrorResult& aRv)
3000 MOZ_ASSERT(NS_IsMainThread());
3002 if (!mTransaction->IsOpen()) {
3003 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
3004 return nullptr;
3007 nsRefPtr<IDBKeyRange> keyRange;
3008 aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
3009 ENSURE_SUCCESS(aRv, nullptr);
3011 IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
3013 return OpenKeyCursorInternal(keyRange, static_cast<size_t>(direction), aRv);
3016 inline nsresult
3017 CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
3019 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3020 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3022 PROFILER_LABEL("IDBObjectStore", "CopyData",
3023 js::ProfileEntry::Category::STORAGE);
3025 nsresult rv;
3027 do {
3028 char copyBuffer[FILE_COPY_BUFFER_SIZE];
3030 uint32_t numRead;
3031 rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
3032 NS_ENSURE_SUCCESS(rv, rv);
3034 if (!numRead) {
3035 break;
3038 uint32_t numWrite;
3039 rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
3040 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
3041 rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
3043 NS_ENSURE_SUCCESS(rv, rv);
3045 NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
3046 } while (true);
3048 rv = aOutputStream->Flush();
3049 NS_ENSURE_SUCCESS(rv, rv);
3051 return NS_OK;
3054 void
3055 ObjectStoreHelper::ReleaseMainThreadObjects()
3057 mObjectStore = nullptr;
3058 AsyncConnectionHelper::ReleaseMainThreadObjects();
3061 nsresult
3062 ObjectStoreHelper::Dispatch(nsIEventTarget* aDatabaseThread)
3064 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3066 PROFILER_MAIN_THREAD_LABEL("ObjectStoreHelper", "Dispatch",
3067 js::ProfileEntry::Category::STORAGE);
3069 if (IndexedDatabaseManager::IsMainProcess()) {
3070 return AsyncConnectionHelper::Dispatch(aDatabaseThread);
3073 // If we've been invalidated then there's no point sending anything to the
3074 // parent process.
3075 if (mObjectStore->Transaction()->Database()->IsInvalidated()) {
3076 IDB_REPORT_INTERNAL_ERR();
3077 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3080 IndexedDBObjectStoreChild* objectStoreActor = mObjectStore->GetActorChild();
3081 NS_ASSERTION(objectStoreActor, "Must have an actor here!");
3083 ObjectStoreRequestParams params;
3085 // Our "parent" process may be either the root process or another content
3086 // process if this indexedDB is managed by a PBrowser that is managed by a
3087 // PContentBridge. We need to find which one it is so that we can create
3088 // PBlobs that are managed by the right nsIContentChild.
3089 IndexedDBChild* rootActor =
3090 static_cast<IndexedDBChild*>(objectStoreActor->Manager()->
3091 Manager()->Manager());
3092 nsIContentChild* blobCreator;
3093 if (rootActor->GetManagerContent()) {
3094 blobCreator = rootActor->GetManagerContent();
3095 } else {
3096 blobCreator = rootActor->GetManagerTab()->Manager();
3099 nsresult rv = PackArgumentsForParentProcess(params, blobCreator);
3100 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3102 NoDispatchEventTarget target;
3103 rv = AsyncConnectionHelper::Dispatch(&target);
3104 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3106 mActor =
3107 new IndexedDBObjectStoreRequestChild(this, mObjectStore, params.type());
3108 objectStoreActor->SendPIndexedDBRequestConstructor(mActor, params);
3110 return NS_OK;
3113 void
3114 NoRequestObjectStoreHelper::ReleaseMainThreadObjects()
3116 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3117 mObjectStore = nullptr;
3118 AsyncConnectionHelper::ReleaseMainThreadObjects();
3121 nsresult
3122 NoRequestObjectStoreHelper::UnpackResponseFromParentProcess(
3123 const ResponseValue& aResponseValue)
3125 MOZ_CRASH();
3128 AsyncConnectionHelper::ChildProcessSendResult
3129 NoRequestObjectStoreHelper::SendResponseToChildProcess(nsresult aResultCode)
3131 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3132 return Success_NotSent;
3135 nsresult
3136 NoRequestObjectStoreHelper::OnSuccess()
3138 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3139 return NS_OK;
3142 void
3143 NoRequestObjectStoreHelper::OnError()
3145 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3146 mTransaction->Abort(GetResultCode());
3149 // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
3150 uint64_t
3151 ReinterpretDoubleAsUInt64(double d)
3153 union {
3154 double d;
3155 uint64_t u;
3156 } pun;
3157 pun.d = d;
3158 return pun.u;
3161 nsresult
3162 AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
3164 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3165 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3166 NS_ASSERTION(aConnection, "Passed a null connection!");
3168 PROFILER_LABEL("AddHelper", "DoDatabaseWork",
3169 js::ProfileEntry::Category::STORAGE);
3171 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
3172 NS_WARNING("Refusing to add more data because disk space is low!");
3173 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
3176 nsresult rv;
3177 bool keyUnset = mKey.IsUnset();
3178 int64_t osid = mObjectStore->Id();
3179 const KeyPath& keyPath = mObjectStore->GetKeyPath();
3181 // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
3182 // specified we should never have a collision and so it shouldn't matter
3183 // if we allow overwrite or not. By not allowing overwrite we raise
3184 // detectable errors rather than corrupting data
3185 nsCOMPtr<mozIStorageStatement> stmt = !mOverwrite || keyUnset ?
3186 mTransaction->GetCachedStatement(
3187 "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
3188 "VALUES (:osid, :key_value, :data, :file_ids)") :
3189 mTransaction->GetCachedStatement(
3190 "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, "
3191 "file_ids) "
3192 "VALUES (:osid, :key_value, :data, :file_ids)");
3193 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3195 mozStorageStatementScoper scoper(stmt);
3197 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
3198 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3200 NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(),
3201 "Should have key unless autoincrement");
3203 int64_t autoIncrementNum = 0;
3205 if (mObjectStore->IsAutoIncrement()) {
3206 if (keyUnset) {
3207 autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId;
3209 MOZ_ASSERT(autoIncrementNum > 0,
3210 "Generated key must always be a positive integer");
3212 if (autoIncrementNum > (1LL << 53)) {
3213 IDB_REPORT_INTERNAL_ERR();
3214 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3217 mKey.SetFromInteger(autoIncrementNum);
3219 else if (mKey.IsFloat() &&
3220 mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) {
3221 autoIncrementNum = floor(mKey.ToFloat());
3224 if (keyUnset && keyPath.IsValid()) {
3225 // Special case where someone put an object into an autoIncrement'ing
3226 // objectStore with no key in its keyPath set. We needed to figure out
3227 // which row id we would get above before we could set that properly.
3229 LittleEndian::writeUint64((char*)mCloneWriteInfo.mCloneBuffer.data() +
3230 mCloneWriteInfo.mOffsetToKeyProp,
3231 ReinterpretDoubleAsUInt64(static_cast<double>(
3232 autoIncrementNum)));
3236 mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value"));
3239 // Compress the bytes before adding into the database.
3240 const char* uncompressed =
3241 reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
3242 size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
3244 // We don't have a smart pointer class that calls moz_free, so we need to
3245 // manage | compressed | manually.
3247 size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
3248 // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage
3249 // expects to be able to free the adopted pointer with NS_Free.
3250 char* compressed = (char*)moz_malloc(compressedLength);
3251 NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY);
3253 snappy::RawCompress(uncompressed, uncompressedLength, compressed,
3254 &compressedLength);
3256 uint8_t* dataBuffer = reinterpret_cast<uint8_t*>(compressed);
3257 size_t dataBufferLength = compressedLength;
3259 // If this call succeeds, | compressed | is now owned by the statement, and
3260 // we are no longer responsible for it.
3261 rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer,
3262 dataBufferLength);
3263 if (NS_FAILED(rv)) {
3264 moz_free(compressed);
3266 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3269 // Handle blobs
3270 uint32_t length = mCloneWriteInfo.mFiles.Length();
3271 if (length) {
3272 nsRefPtr<FileManager> fileManager = mDatabase->Manager();
3274 nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
3275 IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3277 nsCOMPtr<nsIFile> journalDirectory = fileManager->EnsureJournalDirectory();
3278 IDB_ENSURE_TRUE(journalDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3280 nsAutoString fileIds;
3282 for (uint32_t index = 0; index < length; index++) {
3283 StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index];
3285 FileInfo* fileInfo = cloneFile.mFileInfo;
3286 nsIInputStream* inputStream = cloneFile.mInputStream;
3288 int64_t id = fileInfo->Id();
3289 if (inputStream) {
3290 // Create a journal file first
3291 nsCOMPtr<nsIFile> nativeFile =
3292 fileManager->GetFileForId(journalDirectory, id);
3293 IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3295 rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
3296 IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3298 // Now we can copy the blob
3299 nativeFile = fileManager->GetFileForId(directory, id);
3300 IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3302 IDBDatabase* database = mObjectStore->Transaction()->Database();
3303 nsRefPtr<FileOutputStream> outputStream =
3304 FileOutputStream::Create(database->Type(), database->Group(),
3305 database->Origin(), nativeFile);
3306 IDB_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3308 rv = CopyData(inputStream, outputStream);
3309 if (NS_FAILED(rv) &&
3310 NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
3311 IDB_REPORT_INTERNAL_ERR();
3312 rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3314 NS_ENSURE_SUCCESS(rv, rv);
3316 cloneFile.mFile->AddFileInfo(fileInfo);
3319 if (index) {
3320 fileIds.Append(' ');
3322 fileIds.AppendInt(id);
3325 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
3327 else {
3328 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
3330 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3332 rv = stmt->Execute();
3333 if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
3334 NS_ASSERTION(!keyUnset, "Generated key had a collision!?");
3335 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
3337 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3339 int64_t objectDataId;
3340 rv = aConnection->GetLastInsertRowID(&objectDataId);
3341 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3343 // Update our indexes if needed.
3344 if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) {
3345 rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite,
3346 objectDataId, mIndexUpdateInfo);
3347 if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
3348 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
3350 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3353 if (autoIncrementNum) {
3354 mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1;
3357 return NS_OK;
3360 nsresult
3361 AddHelper::GetSuccessResult(JSContext* aCx,
3362 JS::MutableHandle<JS::Value> aVal)
3364 NS_ASSERTION(!mKey.IsUnset(), "Badness!");
3366 mCloneWriteInfo.mCloneBuffer.clear();
3368 return mKey.ToJSVal(aCx, aVal);
3371 void
3372 AddHelper::ReleaseMainThreadObjects()
3374 IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
3375 ObjectStoreHelper::ReleaseMainThreadObjects();
3378 nsresult
3379 AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
3380 nsIContentChild* aBlobCreator)
3382 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3383 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3384 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
3386 PROFILER_MAIN_THREAD_LABEL("AddHelper", "PackArgumentsForParentProcess",
3387 js::ProfileEntry::Category::STORAGE);
3389 AddPutParams commonParams;
3390 commonParams.cloneInfo() = mCloneWriteInfo;
3391 commonParams.key() = mKey;
3392 commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo);
3394 const nsTArray<StructuredCloneFile>& files = mCloneWriteInfo.mFiles;
3396 if (!files.IsEmpty()) {
3397 uint32_t fileCount = files.Length();
3399 InfallibleTArray<PBlobChild*>& blobsChild = commonParams.blobsChild();
3400 blobsChild.SetCapacity(fileCount);
3402 NS_ASSERTION(aBlobCreator, "This should never be null!");
3404 for (uint32_t index = 0; index < fileCount; index++) {
3405 const StructuredCloneFile& file = files[index];
3407 NS_ASSERTION(file.mFile, "This should never be null!");
3408 NS_ASSERTION(!file.mFileInfo, "This is not yet supported!");
3410 BlobChild* actor =
3411 aBlobCreator->GetOrCreateActorForBlob(file.mFile);
3412 if (!actor) {
3413 IDB_REPORT_INTERNAL_ERR();
3414 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3416 blobsChild.AppendElement(actor);
3420 if (mOverwrite) {
3421 PutParams putParams;
3422 putParams.commonParams() = commonParams;
3423 aParams = putParams;
3425 else {
3426 AddParams addParams;
3427 addParams.commonParams() = commonParams;
3428 aParams = addParams;
3431 return NS_OK;
3434 AsyncConnectionHelper::ChildProcessSendResult
3435 AddHelper::SendResponseToChildProcess(nsresult aResultCode)
3437 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3438 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3440 PROFILER_MAIN_THREAD_LABEL("AddHelper", "SendResponseToChildProcess",
3441 js::ProfileEntry::Category::STORAGE);
3443 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3444 NS_ASSERTION(actor, "How did we get this far without an actor?");
3446 ResponseValue response;
3447 if (NS_FAILED(aResultCode)) {
3448 response = aResultCode;
3450 else if (mOverwrite) {
3451 PutResponse putResponse;
3452 putResponse.key() = mKey;
3453 response = putResponse;
3455 else {
3456 AddResponse addResponse;
3457 addResponse.key() = mKey;
3458 response = addResponse;
3461 if (!actor->SendResponse(response)) {
3462 return Error;
3465 return Success_Sent;
3468 nsresult
3469 AddHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
3471 NS_ASSERTION(aResponseValue.type() == ResponseValue::TAddResponse ||
3472 aResponseValue.type() == ResponseValue::TPutResponse,
3473 "Bad response type!");
3475 mKey = mOverwrite ?
3476 aResponseValue.get_PutResponse().key() :
3477 aResponseValue.get_AddResponse().key();
3479 return NS_OK;
3482 nsresult
3483 GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
3485 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3486 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3487 NS_ASSERTION(mKeyRange, "Must have a key range here!");
3489 PROFILER_LABEL("GetHelper", "DoDatabaseWork [IDBObjectStore.cpp]",
3490 js::ProfileEntry::Category::STORAGE);
3492 nsCString keyRangeClause;
3493 mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause);
3495 NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
3497 nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
3498 "WHERE object_store_id = :osid") +
3499 keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
3501 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
3502 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3504 mozStorageStatementScoper scoper(stmt);
3506 nsresult rv =
3507 stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->Id());
3508 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3510 rv = mKeyRange->BindToStatement(stmt);
3511 NS_ENSURE_SUCCESS(rv, rv);
3513 bool hasResult;
3514 rv = stmt->ExecuteStep(&hasResult);
3515 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3517 if (hasResult) {
3518 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
3519 mDatabase, mCloneReadInfo);
3520 NS_ENSURE_SUCCESS(rv, rv);
3523 return NS_OK;
3526 nsresult
3527 GetHelper::GetSuccessResult(JSContext* aCx,
3528 JS::MutableHandle<JS::Value> aVal)
3530 bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal);
3532 mCloneReadInfo.mCloneBuffer.clear();
3534 NS_ENSURE_TRUE(result, NS_ERROR_DOM_DATA_CLONE_ERR);
3535 return NS_OK;
3538 void
3539 GetHelper::ReleaseMainThreadObjects()
3541 mKeyRange = nullptr;
3542 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
3543 ObjectStoreHelper::ReleaseMainThreadObjects();
3546 nsresult
3547 GetHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
3548 nsIContentChild* aBlobCreator)
3550 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3551 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3552 NS_ASSERTION(mKeyRange, "This should never be null!");
3553 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
3555 PROFILER_MAIN_THREAD_LABEL("GetHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]",
3556 js::ProfileEntry::Category::STORAGE);
3558 GetParams params;
3560 mKeyRange->ToSerializedKeyRange(params.keyRange());
3562 aParams = params;
3563 return NS_OK;
3566 AsyncConnectionHelper::ChildProcessSendResult
3567 GetHelper::SendResponseToChildProcess(nsresult aResultCode)
3569 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3570 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3572 PROFILER_MAIN_THREAD_LABEL("GetHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]",
3573 js::ProfileEntry::Category::STORAGE);
3575 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3576 NS_ASSERTION(actor, "How did we get this far without an actor?");
3578 InfallibleTArray<PBlobParent*> blobsParent;
3580 if (NS_SUCCEEDED(aResultCode)) {
3581 IDBDatabase* database = mObjectStore->Transaction()->Database();
3582 NS_ASSERTION(database, "This should never be null!");
3584 nsIContentParent* contentParent = database->GetContentParent();
3585 NS_ASSERTION(contentParent, "This should never be null!");
3587 FileManager* fileManager = database->Manager();
3588 NS_ASSERTION(fileManager, "This should never be null!");
3590 const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
3592 aResultCode =
3593 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
3594 blobsParent);
3595 if (NS_FAILED(aResultCode)) {
3596 NS_WARNING("ConvertBlobsToActors failed!");
3600 ResponseValue response;
3601 if (NS_FAILED(aResultCode)) {
3602 response = aResultCode;
3604 else {
3605 GetResponse getResponse;
3606 getResponse.cloneInfo() = mCloneReadInfo;
3607 getResponse.blobsParent().SwapElements(blobsParent);
3608 response = getResponse;
3611 if (!actor->SendResponse(response)) {
3612 return Error;
3615 return Success_Sent;
3618 nsresult
3619 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
3621 NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse,
3622 "Bad response type!");
3624 const GetResponse& getResponse = aResponseValue.get_GetResponse();
3625 const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo();
3627 NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
3628 (cloneInfo.dataLength && cloneInfo.data),
3629 "Inconsistent clone info!");
3631 if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
3632 IDB_WARNING("Failed to copy clone buffer!");
3633 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3636 IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(),
3637 mCloneReadInfo.mFiles);
3638 return NS_OK;
3641 nsresult
3642 DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */)
3644 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3645 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3646 NS_ASSERTION(mKeyRange, "Must have a key range here!");
3648 PROFILER_LABEL("DeleteHelper", "DoDatabaseWork",
3649 js::ProfileEntry::Category::STORAGE);
3651 nsCString keyRangeClause;
3652 mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause);
3654 NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
3656 nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data "
3657 "WHERE object_store_id = :osid") +
3658 keyRangeClause;
3660 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
3661 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3663 mozStorageStatementScoper scoper(stmt);
3665 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
3666 mObjectStore->Id());
3667 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3669 rv = mKeyRange->BindToStatement(stmt);
3670 NS_ENSURE_SUCCESS(rv, rv);
3672 rv = stmt->Execute();
3673 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3675 return NS_OK;
3678 nsresult
3679 DeleteHelper::GetSuccessResult(JSContext* aCx,
3680 JS::MutableHandle<JS::Value> aVal)
3682 aVal.setUndefined();
3683 return NS_OK;
3686 nsresult
3687 DeleteHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
3688 nsIContentChild* aBlobCreator)
3690 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3691 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3692 NS_ASSERTION(mKeyRange, "This should never be null!");
3693 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
3695 PROFILER_MAIN_THREAD_LABEL("DeleteHelper", "PackArgumentsForParentProcess",
3696 js::ProfileEntry::Category::STORAGE);
3698 DeleteParams params;
3700 mKeyRange->ToSerializedKeyRange(params.keyRange());
3702 aParams = params;
3703 return NS_OK;
3706 AsyncConnectionHelper::ChildProcessSendResult
3707 DeleteHelper::SendResponseToChildProcess(nsresult aResultCode)
3709 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3710 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3712 PROFILER_MAIN_THREAD_LABEL("DeleteHelper", "SendResponseToChildProcess",
3713 js::ProfileEntry::Category::STORAGE);
3715 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3716 NS_ASSERTION(actor, "How did we get this far without an actor?");
3718 ResponseValue response;
3719 if (NS_FAILED(aResultCode)) {
3720 response = aResultCode;
3722 else {
3723 response = DeleteResponse();
3726 if (!actor->SendResponse(response)) {
3727 return Error;
3730 return Success_Sent;
3733 nsresult
3734 DeleteHelper::UnpackResponseFromParentProcess(
3735 const ResponseValue& aResponseValue)
3737 NS_ASSERTION(aResponseValue.type() == ResponseValue::TDeleteResponse,
3738 "Bad response type!");
3740 return NS_OK;
3743 nsresult
3744 ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
3746 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3747 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3748 NS_ASSERTION(aConnection, "Passed a null connection!");
3750 PROFILER_LABEL("ClearHelper", "DoDatabaseWork",
3751 js::ProfileEntry::Category::STORAGE);
3753 nsCOMPtr<mozIStorageStatement> stmt =
3754 mTransaction->GetCachedStatement(
3755 NS_LITERAL_CSTRING("DELETE FROM object_data "
3756 "WHERE object_store_id = :osid"));
3757 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3759 mozStorageStatementScoper scoper(stmt);
3761 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
3762 mObjectStore->Id());
3763 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3765 rv = stmt->Execute();
3766 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3768 return NS_OK;
3771 nsresult
3772 ClearHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
3773 nsIContentChild* aBlobCreator)
3775 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3776 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3777 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
3779 PROFILER_MAIN_THREAD_LABEL("ClearHelper", "PackArgumentsForParentProcess",
3780 js::ProfileEntry::Category::STORAGE);
3782 aParams = ClearParams();
3783 return NS_OK;
3786 AsyncConnectionHelper::ChildProcessSendResult
3787 ClearHelper::SendResponseToChildProcess(nsresult aResultCode)
3789 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3790 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3792 PROFILER_MAIN_THREAD_LABEL("ClearHelper", "SendResponseToChildProcess",
3793 js::ProfileEntry::Category::STORAGE);
3795 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3796 NS_ASSERTION(actor, "How did we get this far without an actor?");
3798 ResponseValue response;
3799 if (NS_FAILED(aResultCode)) {
3800 response = aResultCode;
3802 else {
3803 response = ClearResponse();
3806 if (!actor->SendResponse(response)) {
3807 return Error;
3810 return Success_Sent;
3813 nsresult
3814 ClearHelper::UnpackResponseFromParentProcess(
3815 const ResponseValue& aResponseValue)
3817 NS_ASSERTION(aResponseValue.type() == ResponseValue::TClearResponse,
3818 "Bad response type!");
3820 return NS_OK;
3823 nsresult
3824 OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
3826 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3827 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3829 PROFILER_LABEL("OpenCursorHelper", "DoDatabaseWork [IDBObjectStore.cpp]",
3830 js::ProfileEntry::Category::STORAGE);
3832 NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
3834 nsCString keyRangeClause;
3835 if (mKeyRange) {
3836 mKeyRange->GetBindingClause(keyValue, keyRangeClause);
3839 nsAutoCString directionClause;
3840 switch (mDirection) {
3841 case IDBCursor::NEXT:
3842 case IDBCursor::NEXT_UNIQUE:
3843 directionClause.AssignLiteral(" ORDER BY key_value ASC");
3844 break;
3846 case IDBCursor::PREV:
3847 case IDBCursor::PREV_UNIQUE:
3848 directionClause.AssignLiteral(" ORDER BY key_value DESC");
3849 break;
3851 default:
3852 NS_NOTREACHED("Unknown direction type!");
3855 nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids "
3856 "FROM object_data "
3857 "WHERE object_store_id = :id") +
3858 keyRangeClause + directionClause +
3859 NS_LITERAL_CSTRING(" LIMIT 1");
3861 nsCOMPtr<mozIStorageStatement> stmt =
3862 mTransaction->GetCachedStatement(firstQuery);
3863 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3865 mozStorageStatementScoper scoper(stmt);
3867 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
3868 mObjectStore->Id());
3869 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3871 if (mKeyRange) {
3872 rv = mKeyRange->BindToStatement(stmt);
3873 NS_ENSURE_SUCCESS(rv, rv);
3876 bool hasResult;
3877 rv = stmt->ExecuteStep(&hasResult);
3878 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3880 if (!hasResult) {
3881 mKey.Unset();
3882 return NS_OK;
3885 rv = mKey.SetFromStatement(stmt, 0);
3886 NS_ENSURE_SUCCESS(rv, rv);
3888 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
3889 mDatabase, mCloneReadInfo);
3890 NS_ENSURE_SUCCESS(rv, rv);
3892 // Now we need to make the query to get the next match.
3893 keyRangeClause.Truncate();
3894 nsAutoCString continueToKeyRangeClause;
3896 NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
3897 NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
3899 switch (mDirection) {
3900 case IDBCursor::NEXT:
3901 case IDBCursor::NEXT_UNIQUE:
3902 AppendConditionClause(keyValue, currentKey, false, false,
3903 keyRangeClause);
3904 AppendConditionClause(keyValue, currentKey, false, true,
3905 continueToKeyRangeClause);
3906 if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
3907 AppendConditionClause(keyValue, rangeKey, true,
3908 !mKeyRange->IsUpperOpen(), keyRangeClause);
3909 AppendConditionClause(keyValue, rangeKey, true,
3910 !mKeyRange->IsUpperOpen(),
3911 continueToKeyRangeClause);
3912 mRangeKey = mKeyRange->Upper();
3914 break;
3916 case IDBCursor::PREV:
3917 case IDBCursor::PREV_UNIQUE:
3918 AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
3919 AppendConditionClause(keyValue, currentKey, true, true,
3920 continueToKeyRangeClause);
3921 if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
3922 AppendConditionClause(keyValue, rangeKey, false,
3923 !mKeyRange->IsLowerOpen(), keyRangeClause);
3924 AppendConditionClause(keyValue, rangeKey, false,
3925 !mKeyRange->IsLowerOpen(),
3926 continueToKeyRangeClause);
3927 mRangeKey = mKeyRange->Lower();
3929 break;
3931 default:
3932 NS_NOTREACHED("Unknown direction type!");
3935 NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids "
3936 "FROM object_data "
3937 "WHERE object_store_id = :id");
3939 mContinueQuery = queryStart + keyRangeClause + directionClause +
3940 NS_LITERAL_CSTRING(" LIMIT ");
3942 mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
3943 NS_LITERAL_CSTRING(" LIMIT ");
3945 return NS_OK;
3948 nsresult
3949 OpenCursorHelper::EnsureCursor()
3951 if (mCursor || mKey.IsUnset()) {
3952 return NS_OK;
3955 mSerializedCloneReadInfo = mCloneReadInfo;
3957 NS_ASSERTION(mSerializedCloneReadInfo.data &&
3958 mSerializedCloneReadInfo.dataLength,
3959 "Shouldn't be possible!");
3961 nsRefPtr<IDBCursor> cursor =
3962 IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
3963 mRangeKey, mContinueQuery, mContinueToQuery, mKey,
3964 Move(mCloneReadInfo));
3965 IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3967 NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
3969 mCursor.swap(cursor);
3970 return NS_OK;
3973 nsresult
3974 OpenCursorHelper::GetSuccessResult(JSContext* aCx,
3975 JS::MutableHandle<JS::Value> aVal)
3977 nsresult rv = EnsureCursor();
3978 NS_ENSURE_SUCCESS(rv, rv);
3980 if (mCursor) {
3981 rv = WrapNative(aCx, mCursor, aVal);
3982 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3984 else {
3985 aVal.setUndefined();
3988 return NS_OK;
3991 void
3992 OpenCursorHelper::ReleaseMainThreadObjects()
3994 mKeyRange = nullptr;
3995 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
3997 mCursor = nullptr;
3999 // These don't need to be released on the main thread but they're only valid
4000 // as long as mCursor is set.
4001 mSerializedCloneReadInfo.data = nullptr;
4002 mSerializedCloneReadInfo.dataLength = 0;
4004 ObjectStoreHelper::ReleaseMainThreadObjects();
4007 nsresult
4008 OpenCursorHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
4009 nsIContentChild* aBlobCreator)
4011 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4012 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4013 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
4015 PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]",
4016 js::ProfileEntry::Category::STORAGE);
4018 OpenCursorParams params;
4020 if (mKeyRange) {
4021 KeyRange keyRange;
4022 mKeyRange->ToSerializedKeyRange(keyRange);
4023 params.optionalKeyRange() = keyRange;
4025 else {
4026 params.optionalKeyRange() = mozilla::void_t();
4029 params.direction() = mDirection;
4031 aParams = params;
4032 return NS_OK;
4035 AsyncConnectionHelper::ChildProcessSendResult
4036 OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
4038 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4039 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4040 NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
4042 PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]",
4043 js::ProfileEntry::Category::STORAGE);
4045 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
4046 NS_ASSERTION(actor, "How did we get this far without an actor?");
4048 InfallibleTArray<PBlobParent*> blobsParent;
4050 if (NS_SUCCEEDED(aResultCode)) {
4051 IDBDatabase* database = mObjectStore->Transaction()->Database();
4052 NS_ASSERTION(database, "This should never be null!");
4054 nsIContentParent* contentParent = database->GetContentParent();
4055 NS_ASSERTION(contentParent, "This should never be null!");
4057 FileManager* fileManager = database->Manager();
4058 NS_ASSERTION(fileManager, "This should never be null!");
4060 const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
4062 aResultCode =
4063 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
4064 blobsParent);
4065 if (NS_FAILED(aResultCode)) {
4066 NS_WARNING("ConvertBlobsToActors failed!");
4070 if (NS_SUCCEEDED(aResultCode)) {
4071 nsresult rv = EnsureCursor();
4072 if (NS_FAILED(rv)) {
4073 NS_WARNING("EnsureCursor failed!");
4074 aResultCode = rv;
4078 ResponseValue response;
4079 if (NS_FAILED(aResultCode)) {
4080 response = aResultCode;
4082 else {
4083 OpenCursorResponse openCursorResponse;
4085 if (!mCursor) {
4086 openCursorResponse = mozilla::void_t();
4088 else {
4089 IndexedDBObjectStoreParent* objectStoreActor =
4090 mObjectStore->GetActorParent();
4091 NS_ASSERTION(objectStoreActor, "Must have an actor here!");
4093 IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent();
4094 NS_ASSERTION(requestActor, "Must have an actor here!");
4096 NS_ASSERTION(mSerializedCloneReadInfo.data &&
4097 mSerializedCloneReadInfo.dataLength,
4098 "Shouldn't be possible!");
4100 ObjectStoreCursorConstructorParams params;
4101 params.requestParent() = requestActor;
4102 params.direction() = mDirection;
4103 params.key() = mKey;
4104 params.optionalCloneInfo() = mSerializedCloneReadInfo;
4105 params.blobsParent().SwapElements(blobsParent);
4107 if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
4108 return Error;
4112 response = openCursorResponse;
4115 if (!actor->SendResponse(response)) {
4116 return Error;
4119 return Success_Sent;
4122 nsresult
4123 OpenCursorHelper::UnpackResponseFromParentProcess(
4124 const ResponseValue& aResponseValue)
4126 NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse,
4127 "Bad response type!");
4128 NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() ==
4129 OpenCursorResponse::Tvoid_t ||
4130 aResponseValue.get_OpenCursorResponse().type() ==
4131 OpenCursorResponse::TPIndexedDBCursorChild,
4132 "Bad response union type!");
4133 NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
4135 const OpenCursorResponse& response =
4136 aResponseValue.get_OpenCursorResponse();
4138 switch (response.type()) {
4139 case OpenCursorResponse::Tvoid_t:
4140 break;
4142 case OpenCursorResponse::TPIndexedDBCursorChild: {
4143 IndexedDBCursorChild* actor =
4144 static_cast<IndexedDBCursorChild*>(
4145 response.get_PIndexedDBCursorChild());
4147 mCursor = actor->ForgetStrongCursor();
4148 NS_ASSERTION(mCursor, "This should never be null!");
4150 } break;
4152 default:
4153 MOZ_CRASH();
4156 return NS_OK;
4159 nsresult
4160 OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
4162 MOZ_ASSERT(!NS_IsMainThread());
4163 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4165 PROFILER_LABEL("OpenKeyCursorHelper", "DoDatabaseWork [IDBObjectStore.cpp]",
4166 js::ProfileEntry::Category::STORAGE);
4168 NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
4169 NS_NAMED_LITERAL_CSTRING(id, "id");
4170 NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
4172 nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT ") + keyValue +
4173 NS_LITERAL_CSTRING(" FROM object_data WHERE "
4174 "object_store_id = :") +
4177 nsAutoCString keyRangeClause;
4178 if (mKeyRange) {
4179 mKeyRange->GetBindingClause(keyValue, keyRangeClause);
4182 nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue;
4183 switch (mDirection) {
4184 case IDBCursor::NEXT:
4185 case IDBCursor::NEXT_UNIQUE:
4186 directionClause.AppendLiteral(" ASC");
4187 break;
4189 case IDBCursor::PREV:
4190 case IDBCursor::PREV_UNIQUE:
4191 directionClause.AppendLiteral(" DESC");
4192 break;
4194 default:
4195 MOZ_CRASH("Unknown direction type!");
4198 nsCString firstQuery = queryStart + keyRangeClause + directionClause +
4199 openLimit + NS_LITERAL_CSTRING("1");
4201 nsCOMPtr<mozIStorageStatement> stmt =
4202 mTransaction->GetCachedStatement(firstQuery);
4203 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4205 mozStorageStatementScoper scoper(stmt);
4207 nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
4208 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4210 if (mKeyRange) {
4211 rv = mKeyRange->BindToStatement(stmt);
4212 NS_ENSURE_SUCCESS(rv, rv);
4215 bool hasResult;
4216 rv = stmt->ExecuteStep(&hasResult);
4217 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4219 if (!hasResult) {
4220 mKey.Unset();
4221 return NS_OK;
4224 rv = mKey.SetFromStatement(stmt, 0);
4225 NS_ENSURE_SUCCESS(rv, rv);
4227 // Now we need to make the query to get the next match.
4228 keyRangeClause.Truncate();
4229 nsAutoCString continueToKeyRangeClause;
4231 NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
4232 NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
4234 switch (mDirection) {
4235 case IDBCursor::NEXT:
4236 case IDBCursor::NEXT_UNIQUE:
4237 AppendConditionClause(keyValue, currentKey, false, false,
4238 keyRangeClause);
4239 AppendConditionClause(keyValue, currentKey, false, true,
4240 continueToKeyRangeClause);
4241 if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
4242 AppendConditionClause(keyValue, rangeKey, true,
4243 !mKeyRange->IsUpperOpen(), keyRangeClause);
4244 AppendConditionClause(keyValue, rangeKey, true,
4245 !mKeyRange->IsUpperOpen(),
4246 continueToKeyRangeClause);
4247 mRangeKey = mKeyRange->Upper();
4249 break;
4251 case IDBCursor::PREV:
4252 case IDBCursor::PREV_UNIQUE:
4253 AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
4254 AppendConditionClause(keyValue, currentKey, true, true,
4255 continueToKeyRangeClause);
4256 if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
4257 AppendConditionClause(keyValue, rangeKey, false,
4258 !mKeyRange->IsLowerOpen(), keyRangeClause);
4259 AppendConditionClause(keyValue, rangeKey, false,
4260 !mKeyRange->IsLowerOpen(),
4261 continueToKeyRangeClause);
4262 mRangeKey = mKeyRange->Lower();
4264 break;
4266 default:
4267 MOZ_CRASH("Unknown direction type!");
4270 mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit;
4271 mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
4272 openLimit;
4274 return NS_OK;
4277 nsresult
4278 OpenKeyCursorHelper::EnsureCursor()
4280 MOZ_ASSERT(NS_IsMainThread());
4282 PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "EnsureCursor [IDBObjectStore.cpp]",
4283 js::ProfileEntry::Category::STORAGE);
4285 if (mCursor || mKey.IsUnset()) {
4286 return NS_OK;
4289 mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
4290 mRangeKey, mContinueQuery, mContinueToQuery,
4291 mKey);
4292 IDB_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4294 return NS_OK;
4297 nsresult
4298 OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx,
4299 JS::MutableHandle<JS::Value> aVal)
4301 MOZ_ASSERT(NS_IsMainThread());
4303 PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "GetSuccessResult [IDBObjectStore.cpp]",
4304 js::ProfileEntry::Category::STORAGE);
4306 nsresult rv = EnsureCursor();
4307 NS_ENSURE_SUCCESS(rv, rv);
4309 if (mCursor) {
4310 rv = WrapNative(aCx, mCursor, aVal);
4311 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4313 else {
4314 aVal.setUndefined();
4317 return NS_OK;
4320 void
4321 OpenKeyCursorHelper::ReleaseMainThreadObjects()
4323 MOZ_ASSERT(NS_IsMainThread());
4325 mKeyRange = nullptr;
4326 mCursor = nullptr;
4328 ObjectStoreHelper::ReleaseMainThreadObjects();
4331 nsresult
4332 OpenKeyCursorHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
4333 nsIContentChild* aBlobCreator)
4335 MOZ_ASSERT(NS_IsMainThread());
4336 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
4337 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
4339 PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]",
4340 js::ProfileEntry::Category::STORAGE);
4342 OpenKeyCursorParams params;
4344 if (mKeyRange) {
4345 KeyRange keyRange;
4346 mKeyRange->ToSerializedKeyRange(keyRange);
4347 params.optionalKeyRange() = keyRange;
4349 else {
4350 params.optionalKeyRange() = mozilla::void_t();
4353 params.direction() = mDirection;
4355 aParams = params;
4356 return NS_OK;
4359 AsyncConnectionHelper::ChildProcessSendResult
4360 OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
4362 MOZ_ASSERT(NS_IsMainThread());
4363 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4364 MOZ_ASSERT(!mCursor);
4366 PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]",
4367 js::ProfileEntry::Category::STORAGE);
4369 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
4370 MOZ_ASSERT(actor);
4372 if (NS_SUCCEEDED(aResultCode)) {
4373 nsresult rv = EnsureCursor();
4374 if (NS_FAILED(rv)) {
4375 NS_WARNING("EnsureCursor failed!");
4376 aResultCode = rv;
4380 ResponseValue response;
4381 if (NS_FAILED(aResultCode)) {
4382 response = aResultCode;
4383 } else {
4384 OpenCursorResponse openCursorResponse;
4386 if (!mCursor) {
4387 openCursorResponse = mozilla::void_t();
4389 else {
4390 IndexedDBObjectStoreParent* objectStoreActor =
4391 mObjectStore->GetActorParent();
4392 MOZ_ASSERT(objectStoreActor);
4394 IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent();
4395 MOZ_ASSERT(requestActor);
4397 ObjectStoreCursorConstructorParams params;
4398 params.requestParent() = requestActor;
4399 params.direction() = mDirection;
4400 params.key() = mKey;
4401 params.optionalCloneInfo() = mozilla::void_t();
4403 if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
4404 return Error;
4408 response = openCursorResponse;
4411 if (!actor->SendResponse(response)) {
4412 return Error;
4415 return Success_Sent;
4418 nsresult
4419 OpenKeyCursorHelper::UnpackResponseFromParentProcess(
4420 const ResponseValue& aResponseValue)
4422 MOZ_ASSERT(NS_IsMainThread());
4423 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
4424 MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse);
4425 MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() ==
4426 OpenCursorResponse::Tvoid_t ||
4427 aResponseValue.get_OpenCursorResponse().type() ==
4428 OpenCursorResponse::TPIndexedDBCursorChild);
4429 MOZ_ASSERT(!mCursor);
4431 PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "UnpackResponseFromParentProcess [IDBObjectStore.cpp]",
4432 js::ProfileEntry::Category::STORAGE);
4434 const OpenCursorResponse& response =
4435 aResponseValue.get_OpenCursorResponse();
4437 switch (response.type()) {
4438 case OpenCursorResponse::Tvoid_t:
4439 break;
4441 case OpenCursorResponse::TPIndexedDBCursorChild: {
4442 IndexedDBCursorChild* actor =
4443 static_cast<IndexedDBCursorChild*>(
4444 response.get_PIndexedDBCursorChild());
4446 mCursor = actor->ForgetStrongCursor();
4447 NS_ASSERTION(mCursor, "This should never be null!");
4449 } break;
4451 default:
4452 MOZ_CRASH("Unknown response union type!");
4455 return NS_OK;
4458 nsresult
4459 CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
4461 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4462 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4464 PROFILER_LABEL("CreateIndexHelper", "DoDatabaseWork",
4465 js::ProfileEntry::Category::STORAGE);
4467 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
4468 NS_WARNING("Refusing to create index because disk space is low!");
4469 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4472 // Insert the data into the database.
4473 nsCOMPtr<mozIStorageStatement> stmt =
4474 mTransaction->GetCachedStatement(
4475 "INSERT INTO object_store_index (id, name, key_path, unique_index, "
4476 "multientry, object_store_id) "
4477 "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)"
4479 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4481 mozStorageStatementScoper scoper(stmt);
4483 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
4484 mIndex->Id());
4485 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4487 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name());
4488 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4490 nsAutoString keyPathSerialization;
4491 mIndex->GetKeyPath().SerializeToString(keyPathSerialization);
4492 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
4493 keyPathSerialization);
4494 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4496 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"),
4497 mIndex->IsUnique() ? 1 : 0);
4498 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4500 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"),
4501 mIndex->IsMultiEntry() ? 1 : 0);
4502 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4504 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
4505 mIndex->ObjectStore()->Id());
4506 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4508 if (NS_FAILED(stmt->Execute())) {
4509 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
4512 #ifdef DEBUG
4514 int64_t id;
4515 aConnection->GetLastInsertRowID(&id);
4516 NS_ASSERTION(mIndex->Id() == id, "Bad index id!");
4518 #endif
4520 // Now we need to populate the index with data from the object store.
4521 rv = InsertDataFromObjectStore(aConnection);
4522 if (NS_FAILED(rv)) {
4523 return rv;
4526 return NS_OK;
4529 void
4530 CreateIndexHelper::ReleaseMainThreadObjects()
4532 mIndex = nullptr;
4533 NoRequestObjectStoreHelper::ReleaseMainThreadObjects();
4536 nsresult
4537 CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
4539 nsCOMPtr<mozIStorageStatement> stmt =
4540 mTransaction->GetCachedStatement(
4541 NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM "
4542 "object_data WHERE object_store_id = :osid"));
4543 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4545 mozStorageStatementScoper scoper(stmt);
4547 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
4548 mIndex->ObjectStore()->Id());
4549 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4551 IDB_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX,
4552 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4554 bool hasResult;
4555 rv = stmt->ExecuteStep(&hasResult);
4556 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4557 if (!hasResult) {
4558 // Bail early if we have no data to avoid creating the below runtime
4559 return NS_OK;
4562 ThreadLocalJSRuntime* tlsEntry =
4563 reinterpret_cast<ThreadLocalJSRuntime*>(PR_GetThreadPrivate(sTLSIndex));
4565 if (!tlsEntry) {
4566 tlsEntry = ThreadLocalJSRuntime::Create();
4567 IDB_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4569 PR_SetThreadPrivate(sTLSIndex, tlsEntry);
4572 JSContext* cx = tlsEntry->Context();
4573 JSAutoRequest ar(cx);
4574 JSAutoCompartment ac(cx, tlsEntry->Global());
4576 do {
4577 StructuredCloneReadInfo cloneReadInfo;
4578 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
4579 mDatabase, cloneReadInfo);
4580 NS_ENSURE_SUCCESS(rv, rv);
4582 JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
4584 JSStructuredCloneCallbacks callbacks = {
4585 IDBObjectStore::StructuredCloneReadCallback<CreateIndexDeserializationTraits>,
4586 nullptr,
4587 nullptr,
4588 nullptr,
4589 nullptr,
4590 nullptr
4593 JS::Rooted<JS::Value> clone(cx);
4594 if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) {
4595 NS_WARNING("Failed to deserialize structured clone data!");
4596 return NS_ERROR_DOM_DATA_CLONE_ERR;
4599 nsTArray<IndexUpdateInfo> updateInfo;
4600 rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(),
4601 mIndex->GetKeyPath(),
4602 mIndex->IsUnique(),
4603 mIndex->IsMultiEntry(),
4604 tlsEntry->Context(),
4605 clone, updateInfo);
4606 NS_ENSURE_SUCCESS(rv, rv);
4608 int64_t objectDataID = stmt->AsInt64(0);
4610 Key key;
4611 rv = key.SetFromStatement(stmt, 3);
4612 NS_ENSURE_SUCCESS(rv, rv);
4614 rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(),
4615 key, false, objectDataID, updateInfo);
4616 NS_ENSURE_SUCCESS(rv, rv);
4618 } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult);
4619 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4621 return NS_OK;
4624 void
4625 CreateIndexHelper::DestroyTLSEntry(void* aPtr)
4627 delete reinterpret_cast<ThreadLocalJSRuntime *>(aPtr);
4630 nsresult
4631 DeleteIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
4633 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4634 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4636 PROFILER_LABEL("DeleteIndexHelper", "DoDatabaseWork",
4637 js::ProfileEntry::Category::STORAGE);
4639 nsCOMPtr<mozIStorageStatement> stmt =
4640 mTransaction->GetCachedStatement(
4641 "DELETE FROM object_store_index "
4642 "WHERE name = :name "
4644 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4646 mozStorageStatementScoper scoper(stmt);
4648 nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName);
4649 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4651 if (NS_FAILED(stmt->Execute())) {
4652 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
4655 return NS_OK;
4658 nsresult
4659 GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
4661 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4662 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4664 PROFILER_LABEL("GetAllHelper", "DoDatabaseWork [IDBObjectStore.cpp]",
4665 js::ProfileEntry::Category::STORAGE);
4667 NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
4668 NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
4670 nsAutoCString keyRangeClause;
4671 if (mKeyRange) {
4672 if (!mKeyRange->Lower().IsUnset()) {
4673 keyRangeClause = NS_LITERAL_CSTRING(" AND key_value");
4674 if (mKeyRange->IsLowerOpen()) {
4675 keyRangeClause.AppendLiteral(" > :");
4677 else {
4678 keyRangeClause.AppendLiteral(" >= :");
4680 keyRangeClause.Append(lowerKeyName);
4683 if (!mKeyRange->Upper().IsUnset()) {
4684 keyRangeClause += NS_LITERAL_CSTRING(" AND key_value");
4685 if (mKeyRange->IsUpperOpen()) {
4686 keyRangeClause.AppendLiteral(" < :");
4688 else {
4689 keyRangeClause.AppendLiteral(" <= :");
4691 keyRangeClause.Append(upperKeyName);
4695 nsAutoCString limitClause;
4696 if (mLimit != UINT32_MAX) {
4697 limitClause.AssignLiteral(" LIMIT ");
4698 limitClause.AppendInt(mLimit);
4701 nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
4702 "WHERE object_store_id = :osid") +
4703 keyRangeClause +
4704 NS_LITERAL_CSTRING(" ORDER BY key_value ASC") +
4705 limitClause;
4707 mCloneReadInfos.SetCapacity(50);
4709 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
4710 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4712 mozStorageStatementScoper scoper(stmt);
4714 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
4715 mObjectStore->Id());
4716 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4718 if (mKeyRange) {
4719 if (!mKeyRange->Lower().IsUnset()) {
4720 rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
4721 NS_ENSURE_SUCCESS(rv, rv);
4723 if (!mKeyRange->Upper().IsUnset()) {
4724 rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
4725 NS_ENSURE_SUCCESS(rv, rv);
4729 bool hasResult;
4730 while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
4731 if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
4732 mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
4735 StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
4736 NS_ASSERTION(readInfo, "Shouldn't fail since SetCapacity succeeded!");
4738 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
4739 mDatabase, *readInfo);
4740 NS_ENSURE_SUCCESS(rv, rv);
4742 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4744 return NS_OK;
4747 nsresult
4748 GetAllHelper::GetSuccessResult(JSContext* aCx,
4749 JS::MutableHandle<JS::Value> aVal)
4751 NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!");
4753 nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal);
4755 NS_ASSERTION(mCloneReadInfos.IsEmpty(),
4756 "Should have cleared in ConvertToArrayAndCleanup");
4757 NS_ENSURE_SUCCESS(rv, rv);
4759 return NS_OK;
4762 void
4763 GetAllHelper::ReleaseMainThreadObjects()
4765 mKeyRange = nullptr;
4766 for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) {
4767 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]);
4769 ObjectStoreHelper::ReleaseMainThreadObjects();
4772 nsresult
4773 GetAllHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
4774 nsIContentChild* aBlobCreator)
4776 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4777 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4778 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
4780 PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]",
4781 js::ProfileEntry::Category::STORAGE);
4783 GetAllParams params;
4785 if (mKeyRange) {
4786 KeyRange keyRange;
4787 mKeyRange->ToSerializedKeyRange(keyRange);
4788 params.optionalKeyRange() = keyRange;
4790 else {
4791 params.optionalKeyRange() = mozilla::void_t();
4794 params.limit() = mLimit;
4796 aParams = params;
4797 return NS_OK;
4800 AsyncConnectionHelper::ChildProcessSendResult
4801 GetAllHelper::SendResponseToChildProcess(nsresult aResultCode)
4803 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4804 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4806 PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]",
4807 js::ProfileEntry::Category::STORAGE);
4809 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
4810 NS_ASSERTION(actor, "How did we get this far without an actor?");
4812 GetAllResponse getAllResponse;
4813 if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) {
4814 IDBDatabase* database = mObjectStore->Transaction()->Database();
4815 NS_ASSERTION(database, "This should never be null!");
4817 nsIContentParent* contentParent = database->GetContentParent();
4818 NS_ASSERTION(contentParent, "This should never be null!");
4820 FileManager* fileManager = database->Manager();
4821 NS_ASSERTION(fileManager, "This should never be null!");
4823 uint32_t length = mCloneReadInfos.Length();
4825 InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
4826 getAllResponse.cloneInfos();
4827 infos.SetCapacity(length);
4829 InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
4830 blobArrays.SetCapacity(length);
4832 for (uint32_t index = 0;
4833 NS_SUCCEEDED(aResultCode) && index < length;
4834 index++) {
4835 // Append the structured clone data.
4836 const StructuredCloneReadInfo& clone = mCloneReadInfos[index];
4837 SerializedStructuredCloneReadInfo* info = infos.AppendElement();
4838 *info = clone;
4840 // Now take care of the files.
4841 const nsTArray<StructuredCloneFile>& files = clone.mFiles;
4842 BlobArray* blobArray = blobArrays.AppendElement();
4843 InfallibleTArray<PBlobParent*>& blobs = blobArray->blobsParent();
4845 aResultCode =
4846 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
4847 blobs);
4848 if (NS_FAILED(aResultCode)) {
4849 NS_WARNING("ConvertBlobsToActors failed!");
4850 break;
4855 ResponseValue response;
4856 if (NS_FAILED(aResultCode)) {
4857 response = aResultCode;
4859 else {
4860 response = getAllResponse;
4863 if (!actor->SendResponse(response)) {
4864 return Error;
4867 return Success_Sent;
4870 nsresult
4871 GetAllHelper::UnpackResponseFromParentProcess(
4872 const ResponseValue& aResponseValue)
4874 NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse,
4875 "Bad response type!");
4877 const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse();
4878 const InfallibleTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
4879 getAllResponse.cloneInfos();
4880 const InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
4882 mCloneReadInfos.SetCapacity(cloneInfos.Length());
4884 for (uint32_t index = 0; index < cloneInfos.Length(); index++) {
4885 const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index];
4886 const InfallibleTArray<PBlobChild*>& blobs = blobArrays[index].blobsChild();
4888 StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement();
4889 if (!destInfo->SetFromSerialized(srcInfo)) {
4890 IDB_WARNING("Failed to copy clone buffer!");
4891 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4894 IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles);
4897 return NS_OK;
4900 nsresult
4901 GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
4903 MOZ_ASSERT(!NS_IsMainThread());
4904 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4906 PROFILER_LABEL("GetAllKeysHelper", "DoDatabaseWork [IDObjectStore.cpp]",
4907 js::ProfileEntry::Category::STORAGE);
4909 NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
4911 nsAutoCString keyRangeClause;
4912 if (mKeyRange) {
4913 mKeyRange->GetBindingClause(keyValue, keyRangeClause);
4916 nsAutoCString limitClause;
4917 if (mLimit != UINT32_MAX) {
4918 limitClause = NS_LITERAL_CSTRING(" LIMIT ");
4919 limitClause.AppendInt(mLimit);
4922 NS_NAMED_LITERAL_CSTRING(osid, "osid");
4924 nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyValue +
4925 NS_LITERAL_CSTRING(" FROM object_data WHERE "
4926 "object_store_id = :") +
4927 osid + keyRangeClause +
4928 NS_LITERAL_CSTRING(" ORDER BY key_value ASC") +
4929 limitClause;
4931 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
4932 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4934 mozStorageStatementScoper scoper(stmt);
4936 nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id());
4937 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4939 if (mKeyRange) {
4940 rv = mKeyRange->BindToStatement(stmt);
4941 NS_ENSURE_SUCCESS(rv, rv);
4944 mKeys.SetCapacity(std::min<uint32_t>(50, mLimit));
4946 bool hasResult;
4947 while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
4948 if (mKeys.Capacity() == mKeys.Length()) {
4949 mKeys.SetCapacity(mKeys.Capacity() * 2);
4952 Key* key = mKeys.AppendElement();
4953 NS_ASSERTION(key, "This shouldn't fail!");
4955 rv = key->SetFromStatement(stmt, 0);
4956 NS_ENSURE_SUCCESS(rv, rv);
4958 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4960 return NS_OK;
4963 nsresult
4964 GetAllKeysHelper::GetSuccessResult(JSContext* aCx,
4965 JS::MutableHandle<JS::Value> aVal)
4967 MOZ_ASSERT(NS_IsMainThread());
4968 MOZ_ASSERT(mKeys.Length() <= mLimit);
4970 PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "GetSuccessResult [IDBObjectStore.cpp]",
4971 js::ProfileEntry::Category::STORAGE);
4973 nsTArray<Key> keys;
4974 mKeys.SwapElements(keys);
4976 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
4977 if (!array) {
4978 IDB_WARNING("Failed to make array!");
4979 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4982 if (!keys.IsEmpty()) {
4983 if (!JS_SetArrayLength(aCx, array, keys.Length())) {
4984 IDB_WARNING("Failed to set array length!");
4985 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4988 for (uint32_t index = 0, count = keys.Length(); index < count; index++) {
4989 const Key& key = keys[index];
4990 MOZ_ASSERT(!key.IsUnset());
4992 JS::Rooted<JS::Value> value(aCx);
4993 nsresult rv = key.ToJSVal(aCx, &value);
4994 if (NS_FAILED(rv)) {
4995 NS_WARNING("Failed to get jsval for key!");
4996 return rv;
4999 if (!JS_SetElement(aCx, array, index, value)) {
5000 IDB_WARNING("Failed to set array element!");
5001 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
5006 aVal.setObject(*array);
5007 return NS_OK;
5010 void
5011 GetAllKeysHelper::ReleaseMainThreadObjects()
5013 MOZ_ASSERT(NS_IsMainThread());
5015 mKeyRange = nullptr;
5017 ObjectStoreHelper::ReleaseMainThreadObjects();
5020 nsresult
5021 GetAllKeysHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
5022 nsIContentChild* aBlobCreator)
5024 MOZ_ASSERT(NS_IsMainThread());
5025 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
5026 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
5028 PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]",
5029 js::ProfileEntry::Category::STORAGE);
5031 GetAllKeysParams params;
5033 if (mKeyRange) {
5034 KeyRange keyRange;
5035 mKeyRange->ToSerializedKeyRange(keyRange);
5036 params.optionalKeyRange() = keyRange;
5037 } else {
5038 params.optionalKeyRange() = mozilla::void_t();
5041 params.limit() = mLimit;
5043 aParams = params;
5044 return NS_OK;
5047 AsyncConnectionHelper::ChildProcessSendResult
5048 GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode)
5050 MOZ_ASSERT(NS_IsMainThread());
5051 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
5053 PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]",
5054 js::ProfileEntry::Category::STORAGE);
5056 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
5057 MOZ_ASSERT(actor);
5059 ResponseValue response;
5060 if (NS_FAILED(aResultCode)) {
5061 response = aResultCode;
5063 else {
5064 GetAllKeysResponse getAllKeysResponse;
5065 getAllKeysResponse.keys().AppendElements(mKeys);
5066 response = getAllKeysResponse;
5069 if (!actor->SendResponse(response)) {
5070 return Error;
5073 return Success_Sent;
5076 nsresult
5077 GetAllKeysHelper::UnpackResponseFromParentProcess(
5078 const ResponseValue& aResponseValue)
5080 MOZ_ASSERT(NS_IsMainThread());
5081 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
5082 MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse);
5084 mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys());
5085 return NS_OK;
5088 nsresult
5089 CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
5091 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
5092 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
5094 PROFILER_LABEL("CountHelper", "DoDatabaseWork [IDBObjectStore.cpp]",
5095 js::ProfileEntry::Category::STORAGE);
5097 NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
5098 NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
5100 nsAutoCString keyRangeClause;
5101 if (mKeyRange) {
5102 if (!mKeyRange->Lower().IsUnset()) {
5103 keyRangeClause = NS_LITERAL_CSTRING(" AND key_value");
5104 if (mKeyRange->IsLowerOpen()) {
5105 keyRangeClause.AppendLiteral(" > :");
5107 else {
5108 keyRangeClause.AppendLiteral(" >= :");
5110 keyRangeClause.Append(lowerKeyName);
5113 if (!mKeyRange->Upper().IsUnset()) {
5114 keyRangeClause += NS_LITERAL_CSTRING(" AND key_value");
5115 if (mKeyRange->IsUpperOpen()) {
5116 keyRangeClause.AppendLiteral(" < :");
5118 else {
5119 keyRangeClause.AppendLiteral(" <= :");
5121 keyRangeClause.Append(upperKeyName);
5125 nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data "
5126 "WHERE object_store_id = :osid") +
5127 keyRangeClause;
5129 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
5130 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5132 mozStorageStatementScoper scoper(stmt);
5134 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
5135 mObjectStore->Id());
5136 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5138 if (mKeyRange) {
5139 if (!mKeyRange->Lower().IsUnset()) {
5140 rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
5141 NS_ENSURE_SUCCESS(rv, rv);
5143 if (!mKeyRange->Upper().IsUnset()) {
5144 rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
5145 NS_ENSURE_SUCCESS(rv, rv);
5149 bool hasResult;
5150 rv = stmt->ExecuteStep(&hasResult);
5151 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5152 IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5154 mCount = stmt->AsInt64(0);
5155 return NS_OK;
5158 nsresult
5159 CountHelper::GetSuccessResult(JSContext* aCx,
5160 JS::MutableHandle<JS::Value> aVal)
5162 aVal.setNumber(static_cast<double>(mCount));
5163 return NS_OK;
5166 void
5167 CountHelper::ReleaseMainThreadObjects()
5169 mKeyRange = nullptr;
5170 ObjectStoreHelper::ReleaseMainThreadObjects();
5173 nsresult
5174 CountHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams,
5175 nsIContentChild* aBlobCreator)
5177 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
5178 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
5179 NS_ASSERTION(aBlobCreator, "Must have a valid creator!");
5181 PROFILER_MAIN_THREAD_LABEL("CountHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]",
5182 js::ProfileEntry::Category::STORAGE);
5184 CountParams params;
5186 if (mKeyRange) {
5187 KeyRange keyRange;
5188 mKeyRange->ToSerializedKeyRange(keyRange);
5189 params.optionalKeyRange() = keyRange;
5191 else {
5192 params.optionalKeyRange() = mozilla::void_t();
5195 aParams = params;
5196 return NS_OK;
5199 AsyncConnectionHelper::ChildProcessSendResult
5200 CountHelper::SendResponseToChildProcess(nsresult aResultCode)
5202 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
5203 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
5205 PROFILER_MAIN_THREAD_LABEL("CountHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]",
5206 js::ProfileEntry::Category::STORAGE);
5208 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
5209 NS_ASSERTION(actor, "How did we get this far without an actor?");
5211 ResponseValue response;
5212 if (NS_FAILED(aResultCode)) {
5213 response = aResultCode;
5215 else {
5216 CountResponse countResponse = mCount;
5217 response = countResponse;
5220 if (!actor->SendResponse(response)) {
5221 return Error;
5224 return Success_Sent;
5227 nsresult
5228 CountHelper::UnpackResponseFromParentProcess(
5229 const ResponseValue& aResponseValue)
5231 NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse,
5232 "Bad response type!");
5234 mCount = aResponseValue.get_CountResponse().count();
5235 return NS_OK;