Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/ContentParent.h"
18 #include "mozilla/dom/FileHandleBinding.h"
19 #include "mozilla/dom/StructuredCloneTags.h"
20 #include "mozilla/dom/ipc/Blob.h"
21 #include "mozilla/dom/quota/FileStreams.h"
22 #include "mozilla/Endian.h"
23 #include "mozilla/storage.h"
24 #include "nsContentUtils.h"
25 #include "nsDOMClassInfo.h"
26 #include "nsDOMFile.h"
27 #include "mozilla/dom/DOMStringList.h"
28 #include "nsJSUtils.h"
29 #include "nsServiceManagerUtils.h"
30 #include "nsThreadUtils.h"
31 #include "snappy/snappy.h"
33 #include "AsyncConnectionHelper.h"
34 #include "IDBCursor.h"
35 #include "IDBEvents.h"
36 #include "IDBFileHandle.h"
37 #include "IDBIndex.h"
38 #include "IDBKeyRange.h"
39 #include "IDBTransaction.h"
40 #include "DatabaseInfo.h"
41 #include "KeyPath.h"
42 #include "ProfilerHelpers.h"
43 #include "ReportInternalError.h"
45 #include "ipc/IndexedDBChild.h"
46 #include "ipc/IndexedDBParent.h"
48 #include "IndexedDatabaseInlines.h"
49 #include "nsCharSeparatedTokenizer.h"
51 #define FILE_COPY_BUFFER_SIZE 32768
53 USING_INDEXEDDB_NAMESPACE
54 using namespace mozilla::dom;
55 using namespace mozilla::dom::indexedDB::ipc;
56 using mozilla::dom::quota::FileOutputStream;
57 using mozilla::ErrorResult;
58 using mozilla::fallible_t;
59 using mozilla::LittleEndian;
60 using mozilla::Move;
61 using mozilla::NativeEndian;
63 BEGIN_INDEXEDDB_NAMESPACE
65 struct FileHandleData
66 {
67 nsString type;
68 nsString name;
69 };
71 struct BlobOrFileData
72 {
73 BlobOrFileData()
74 : tag(0), size(0), lastModifiedDate(UINT64_MAX)
75 { }
77 uint32_t tag;
78 uint64_t size;
79 nsString type;
80 nsString name;
81 uint64_t lastModifiedDate;
82 };
84 END_INDEXEDDB_NAMESPACE
86 namespace {
88 inline
89 bool
90 IgnoreNothing(char16_t c)
91 {
92 return false;
93 }
95 class ObjectStoreHelper : public AsyncConnectionHelper
96 {
97 public:
98 ObjectStoreHelper(IDBTransaction* aTransaction,
99 IDBRequest* aRequest,
100 IDBObjectStore* aObjectStore)
101 : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
102 mActor(nullptr)
103 {
104 NS_ASSERTION(aTransaction, "Null transaction!");
105 NS_ASSERTION(aRequest, "Null request!");
106 NS_ASSERTION(aObjectStore, "Null object store!");
107 }
109 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
111 virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE;
113 virtual nsresult
114 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) = 0;
116 virtual nsresult
117 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0;
119 protected:
120 nsRefPtr<IDBObjectStore> mObjectStore;
122 private:
123 IndexedDBObjectStoreRequestChild* mActor;
124 };
126 class NoRequestObjectStoreHelper : public AsyncConnectionHelper
127 {
128 public:
129 NoRequestObjectStoreHelper(IDBTransaction* aTransaction,
130 IDBObjectStore* aObjectStore)
131 : AsyncConnectionHelper(aTransaction, nullptr), mObjectStore(aObjectStore)
132 {
133 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
134 NS_ASSERTION(aTransaction, "Null transaction!");
135 NS_ASSERTION(aObjectStore, "Null object store!");
136 }
138 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
140 virtual nsresult UnpackResponseFromParentProcess(
141 const ResponseValue& aResponseValue)
142 MOZ_OVERRIDE;
144 virtual ChildProcessSendResult
145 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
147 virtual nsresult OnSuccess() MOZ_OVERRIDE;
149 virtual void OnError() MOZ_OVERRIDE;
151 protected:
152 nsRefPtr<IDBObjectStore> mObjectStore;
153 };
155 class AddHelper : public ObjectStoreHelper
156 {
157 public:
158 AddHelper(IDBTransaction* aTransaction,
159 IDBRequest* aRequest,
160 IDBObjectStore* aObjectStore,
161 StructuredCloneWriteInfo&& aCloneWriteInfo,
162 const Key& aKey,
163 bool aOverwrite,
164 nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
165 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
166 mCloneWriteInfo(Move(aCloneWriteInfo)),
167 mKey(aKey),
168 mOverwrite(aOverwrite)
169 {
170 mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
171 }
173 ~AddHelper()
174 {
175 IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
176 }
178 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
179 MOZ_OVERRIDE;
181 virtual nsresult GetSuccessResult(JSContext* aCx,
182 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
184 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
186 virtual nsresult
187 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
189 virtual ChildProcessSendResult
190 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
192 virtual nsresult
193 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
194 MOZ_OVERRIDE;
196 private:
197 // These may change in the autoincrement case.
198 StructuredCloneWriteInfo mCloneWriteInfo;
199 Key mKey;
200 nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
201 const bool mOverwrite;
202 };
204 class GetHelper : public ObjectStoreHelper
205 {
206 public:
207 GetHelper(IDBTransaction* aTransaction,
208 IDBRequest* aRequest,
209 IDBObjectStore* aObjectStore,
210 IDBKeyRange* aKeyRange)
211 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
212 mKeyRange(aKeyRange)
213 {
214 NS_ASSERTION(aKeyRange, "Null key range!");
215 }
217 ~GetHelper()
218 {
219 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
220 }
222 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
223 MOZ_OVERRIDE;
225 virtual nsresult GetSuccessResult(JSContext* aCx,
226 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
228 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
230 virtual nsresult
231 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
233 virtual ChildProcessSendResult
234 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
236 virtual nsresult
237 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
238 MOZ_OVERRIDE;
240 protected:
241 // In-params.
242 nsRefPtr<IDBKeyRange> mKeyRange;
244 private:
245 // Out-params.
246 StructuredCloneReadInfo mCloneReadInfo;
247 };
249 class DeleteHelper : public GetHelper
250 {
251 public:
252 DeleteHelper(IDBTransaction* aTransaction,
253 IDBRequest* aRequest,
254 IDBObjectStore* aObjectStore,
255 IDBKeyRange* aKeyRange)
256 : GetHelper(aTransaction, aRequest, aObjectStore, aKeyRange)
257 { }
259 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
260 MOZ_OVERRIDE;
262 virtual nsresult GetSuccessResult(JSContext* aCx,
263 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
265 virtual nsresult
266 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
268 virtual ChildProcessSendResult
269 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
271 virtual nsresult
272 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
273 MOZ_OVERRIDE;
274 };
276 class ClearHelper : public ObjectStoreHelper
277 {
278 public:
279 ClearHelper(IDBTransaction* aTransaction,
280 IDBRequest* aRequest,
281 IDBObjectStore* aObjectStore)
282 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore)
283 { }
285 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
286 MOZ_OVERRIDE;
288 virtual nsresult
289 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
291 virtual ChildProcessSendResult
292 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
294 virtual nsresult
295 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
296 MOZ_OVERRIDE;
297 };
299 class OpenCursorHelper : public ObjectStoreHelper
300 {
301 public:
302 OpenCursorHelper(IDBTransaction* aTransaction,
303 IDBRequest* aRequest,
304 IDBObjectStore* aObjectStore,
305 IDBKeyRange* aKeyRange,
306 IDBCursor::Direction aDirection)
307 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
308 mKeyRange(aKeyRange), mDirection(aDirection)
309 { }
311 ~OpenCursorHelper()
312 {
313 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
314 }
316 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
317 MOZ_OVERRIDE;
319 virtual nsresult GetSuccessResult(JSContext* aCx,
320 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
322 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
324 virtual nsresult
325 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
327 virtual ChildProcessSendResult
328 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
330 virtual nsresult
331 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
332 MOZ_OVERRIDE;
334 private:
335 nsresult EnsureCursor();
337 // In-params.
338 nsRefPtr<IDBKeyRange> mKeyRange;
339 const IDBCursor::Direction mDirection;
341 // Out-params.
342 Key mKey;
343 StructuredCloneReadInfo mCloneReadInfo;
344 nsCString mContinueQuery;
345 nsCString mContinueToQuery;
346 Key mRangeKey;
348 // Only used in the parent process.
349 nsRefPtr<IDBCursor> mCursor;
350 SerializedStructuredCloneReadInfo mSerializedCloneReadInfo;
351 };
353 class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper
354 {
355 public:
356 OpenKeyCursorHelper(IDBTransaction* aTransaction,
357 IDBRequest* aRequest,
358 IDBObjectStore* aObjectStore,
359 IDBKeyRange* aKeyRange,
360 IDBCursor::Direction aDirection)
361 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
362 mKeyRange(aKeyRange), mDirection(aDirection)
363 { }
365 virtual nsresult
366 DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
368 virtual nsresult
369 GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
370 MOZ_OVERRIDE;
372 virtual void
373 ReleaseMainThreadObjects() MOZ_OVERRIDE;
375 virtual nsresult
376 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
378 virtual ChildProcessSendResult
379 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
381 virtual nsresult
382 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
383 MOZ_OVERRIDE;
385 private:
386 ~OpenKeyCursorHelper()
387 { }
389 nsresult EnsureCursor();
391 // In-params.
392 nsRefPtr<IDBKeyRange> mKeyRange;
393 const IDBCursor::Direction mDirection;
395 // Out-params.
396 Key mKey;
397 nsCString mContinueQuery;
398 nsCString mContinueToQuery;
399 Key mRangeKey;
401 // Only used in the parent process.
402 nsRefPtr<IDBCursor> mCursor;
403 };
405 class CreateIndexHelper : public NoRequestObjectStoreHelper
406 {
407 public:
408 CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex)
409 : NoRequestObjectStoreHelper(aTransaction, aIndex->ObjectStore()),
410 mIndex(aIndex)
411 {
412 if (sTLSIndex == BAD_TLS_INDEX) {
413 PR_NewThreadPrivateIndex(&sTLSIndex, DestroyTLSEntry);
414 }
416 NS_ASSERTION(sTLSIndex != BAD_TLS_INDEX,
417 "PR_NewThreadPrivateIndex failed!");
418 }
420 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
421 MOZ_OVERRIDE;
423 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
425 private:
426 nsresult InsertDataFromObjectStore(mozIStorageConnection* aConnection);
428 static void DestroyTLSEntry(void* aPtr);
430 static unsigned sTLSIndex;
432 // In-params.
433 nsRefPtr<IDBIndex> mIndex;
434 };
436 unsigned CreateIndexHelper::sTLSIndex = unsigned(BAD_TLS_INDEX);
438 class DeleteIndexHelper : public NoRequestObjectStoreHelper
439 {
440 public:
441 DeleteIndexHelper(IDBTransaction* aTransaction,
442 IDBObjectStore* aObjectStore,
443 const nsAString& aName)
444 : NoRequestObjectStoreHelper(aTransaction, aObjectStore), mName(aName)
445 { }
447 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
448 MOZ_OVERRIDE;
450 private:
451 // In-params
452 nsString mName;
453 };
455 class GetAllHelper : public ObjectStoreHelper
456 {
457 public:
458 GetAllHelper(IDBTransaction* aTransaction,
459 IDBRequest* aRequest,
460 IDBObjectStore* aObjectStore,
461 IDBKeyRange* aKeyRange,
462 const uint32_t aLimit)
463 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
464 mKeyRange(aKeyRange), mLimit(aLimit)
465 { }
467 ~GetAllHelper()
468 {
469 for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) {
470 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]);
471 }
472 }
474 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
475 MOZ_OVERRIDE;
477 virtual nsresult GetSuccessResult(JSContext* aCx,
478 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
480 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
482 virtual nsresult
483 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
485 virtual ChildProcessSendResult
486 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
488 virtual nsresult
489 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
490 MOZ_OVERRIDE;
492 protected:
493 // In-params.
494 nsRefPtr<IDBKeyRange> mKeyRange;
495 const uint32_t mLimit;
497 private:
498 // Out-params.
499 nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
500 };
502 class GetAllKeysHelper MOZ_FINAL : public ObjectStoreHelper
503 {
504 public:
505 GetAllKeysHelper(IDBTransaction* aTransaction,
506 IDBRequest* aRequest,
507 IDBObjectStore* aObjectStore,
508 IDBKeyRange* aKeyRange,
509 const uint32_t aLimit)
510 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
511 mKeyRange(aKeyRange), mLimit(aLimit)
512 { }
514 virtual nsresult
515 DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE;
517 virtual nsresult
518 GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
519 MOZ_OVERRIDE;
521 virtual void
522 ReleaseMainThreadObjects() MOZ_OVERRIDE;
524 virtual nsresult
525 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
527 virtual ChildProcessSendResult
528 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
530 virtual nsresult
531 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
532 MOZ_OVERRIDE;
534 private:
535 ~GetAllKeysHelper()
536 { }
538 nsRefPtr<IDBKeyRange> mKeyRange;
539 const uint32_t mLimit;
540 nsTArray<Key> mKeys;
541 };
543 class CountHelper : public ObjectStoreHelper
544 {
545 public:
546 CountHelper(IDBTransaction* aTransaction,
547 IDBRequest* aRequest,
548 IDBObjectStore* aObjectStore,
549 IDBKeyRange* aKeyRange)
550 : ObjectStoreHelper(aTransaction, aRequest, aObjectStore),
551 mKeyRange(aKeyRange), mCount(0)
552 { }
554 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
555 MOZ_OVERRIDE;
557 virtual nsresult GetSuccessResult(JSContext* aCx,
558 JS::MutableHandle<JS::Value> aVal) MOZ_OVERRIDE;
560 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
562 virtual nsresult
563 PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams) MOZ_OVERRIDE;
565 virtual ChildProcessSendResult
566 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
568 virtual nsresult
569 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
570 MOZ_OVERRIDE;
572 private:
573 nsRefPtr<IDBKeyRange> mKeyRange;
574 uint64_t mCount;
575 };
577 class MOZ_STACK_CLASS AutoRemoveIndex
578 {
579 public:
580 AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo,
581 const nsAString& aIndexName)
582 : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName)
583 { }
585 ~AutoRemoveIndex()
586 {
587 if (mObjectStoreInfo) {
588 for (uint32_t i = 0; i < mObjectStoreInfo->indexes.Length(); i++) {
589 if (mObjectStoreInfo->indexes[i].name == mIndexName) {
590 mObjectStoreInfo->indexes.RemoveElementAt(i);
591 break;
592 }
593 }
594 }
595 }
597 void forget()
598 {
599 mObjectStoreInfo = nullptr;
600 }
602 private:
603 ObjectStoreInfo* mObjectStoreInfo;
604 nsString mIndexName;
605 };
607 class ThreadLocalJSRuntime
608 {
609 JSRuntime* mRuntime;
610 JSContext* mContext;
611 JSObject* mGlobal;
613 static const JSClass sGlobalClass;
614 static const unsigned sRuntimeHeapSize = 768 * 1024;
616 ThreadLocalJSRuntime()
617 : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr)
618 {
619 MOZ_COUNT_CTOR(ThreadLocalJSRuntime);
620 }
622 nsresult Init()
623 {
624 mRuntime = JS_NewRuntime(sRuntimeHeapSize, JS_NO_HELPER_THREADS);
625 NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY);
627 /*
628 * Not setting this will cause JS_CHECK_RECURSION to report false
629 * positives
630 */
631 JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024);
633 mContext = JS_NewContext(mRuntime, 0);
634 NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY);
636 JSAutoRequest ar(mContext);
638 mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
639 JS::FireOnNewGlobalHook);
640 NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY);
642 js::SetDefaultObjectForContext(mContext, mGlobal);
643 return NS_OK;
644 }
646 public:
647 static ThreadLocalJSRuntime *Create()
648 {
649 ThreadLocalJSRuntime *entry = new ThreadLocalJSRuntime();
650 NS_ENSURE_TRUE(entry, nullptr);
652 if (NS_FAILED(entry->Init())) {
653 delete entry;
654 return nullptr;
655 }
657 return entry;
658 }
660 JSContext *Context() const
661 {
662 return mContext;
663 }
665 JSObject *Global() const
666 {
667 return mGlobal;
668 }
670 ~ThreadLocalJSRuntime()
671 {
672 MOZ_COUNT_DTOR(ThreadLocalJSRuntime);
674 if (mContext) {
675 JS_DestroyContext(mContext);
676 }
678 if (mRuntime) {
679 JS_DestroyRuntime(mRuntime);
680 }
681 }
682 };
684 const JSClass ThreadLocalJSRuntime::sGlobalClass = {
685 "IndexedDBTransactionThreadGlobal",
686 JSCLASS_GLOBAL_FLAGS,
687 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
688 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
689 nullptr, nullptr, nullptr, nullptr,
690 JS_GlobalObjectTraceHook
691 };
693 inline
694 already_AddRefed<IDBRequest>
695 GenerateRequest(IDBObjectStore* aObjectStore)
696 {
697 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
698 IDBDatabase* database = aObjectStore->Transaction()->Database();
699 return IDBRequest::Create(aObjectStore, database,
700 aObjectStore->Transaction());
701 }
703 struct MOZ_STACK_CLASS GetAddInfoClosure
704 {
705 IDBObjectStore* mThis;
706 StructuredCloneWriteInfo& mCloneWriteInfo;
707 JS::Handle<JS::Value> mValue;
708 };
710 nsresult
711 GetAddInfoCallback(JSContext* aCx, void* aClosure)
712 {
713 GetAddInfoClosure* data = static_cast<GetAddInfoClosure*>(aClosure);
715 data->mCloneWriteInfo.mOffsetToKeyProp = 0;
716 data->mCloneWriteInfo.mTransaction = data->mThis->Transaction();
718 if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo, data->mValue)) {
719 return NS_ERROR_DOM_DATA_CLONE_ERR;
720 }
722 return NS_OK;
723 }
725 inline
726 BlobChild*
727 ActorFromRemoteBlob(nsIDOMBlob* aBlob)
728 {
729 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
730 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
732 nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
733 if (remoteBlob) {
734 BlobChild* actor =
735 static_cast<BlobChild*>(static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
736 NS_ASSERTION(actor, "Null actor?!");
737 return actor;
738 }
739 return nullptr;
740 }
742 inline
743 bool
744 ResolveMysteryFile(nsIDOMBlob* aBlob, const nsString& aName,
745 const nsString& aContentType, uint64_t aSize,
746 uint64_t aLastModifiedDate)
747 {
748 BlobChild* actor = ActorFromRemoteBlob(aBlob);
749 if (actor) {
750 return actor->SetMysteryBlobInfo(aName, aContentType,
751 aSize, aLastModifiedDate);
752 }
753 return true;
754 }
756 inline
757 bool
758 ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType,
759 uint64_t aSize)
760 {
761 BlobChild* actor = ActorFromRemoteBlob(aBlob);
762 if (actor) {
763 return actor->SetMysteryBlobInfo(aContentType, aSize);
764 }
765 return true;
766 }
768 class MainThreadDeserializationTraits
769 {
770 public:
771 static JSObject* CreateAndWrapFileHandle(JSContext* aCx,
772 IDBDatabase* aDatabase,
773 StructuredCloneFile& aFile,
774 const FileHandleData& aData)
775 {
776 MOZ_ASSERT(NS_IsMainThread());
778 nsRefPtr<FileInfo>& fileInfo = aFile.mFileInfo;
780 nsRefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(aDatabase,
781 aData.name, aData.type, fileInfo.forget());
783 return fileHandle->WrapObject(aCx);
784 }
786 static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx,
787 IDBDatabase* aDatabase,
788 StructuredCloneFile& aFile,
789 const BlobOrFileData& aData)
790 {
791 MOZ_ASSERT(NS_IsMainThread());
793 MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
794 aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
795 aData.tag == SCTAG_DOM_BLOB);
797 nsresult rv = NS_OK;
799 nsRefPtr<FileInfo>& fileInfo = aFile.mFileInfo;
801 nsCOMPtr<nsIFile> nativeFile;
802 if (!aFile.mFile) {
803 FileManager* fileManager = aDatabase->Manager();
804 NS_ASSERTION(fileManager, "This should never be null!");
806 nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
807 if (!directory) {
808 NS_WARNING("Failed to get directory!");
809 return nullptr;
810 }
812 nativeFile = fileManager->GetFileForId(directory, fileInfo->Id());
813 if (!nativeFile) {
814 NS_WARNING("Failed to get file!");
815 return nullptr;
816 }
817 }
819 if (aData.tag == SCTAG_DOM_BLOB) {
820 nsCOMPtr<nsIDOMBlob> domBlob;
821 if (aFile.mFile) {
822 if (!ResolveMysteryBlob(aFile.mFile, aData.type, aData.size)) {
823 return nullptr;
824 }
825 domBlob = aFile.mFile;
826 }
827 else {
828 domBlob = new nsDOMFileFile(aData.type, aData.size, nativeFile,
829 fileInfo);
830 }
832 JS::Rooted<JS::Value> wrappedBlob(aCx);
833 rv = nsContentUtils::WrapNative(aCx, domBlob, &NS_GET_IID(nsIDOMBlob),
834 &wrappedBlob);
835 if (NS_FAILED(rv)) {
836 NS_WARNING("Failed to wrap native!");
837 return nullptr;
838 }
840 return JSVAL_TO_OBJECT(wrappedBlob);
841 }
843 nsCOMPtr<nsIDOMFile> domFile;
844 if (aFile.mFile) {
845 if (!ResolveMysteryFile(aFile.mFile, aData.name, aData.type, aData.size,
846 aData.lastModifiedDate)) {
847 return nullptr;
848 }
849 domFile = do_QueryInterface(aFile.mFile);
850 NS_ASSERTION(domFile, "This should never fail!");
851 }
852 else {
853 domFile = new nsDOMFileFile(aData.name, aData.type, aData.size,
854 nativeFile, fileInfo);
855 }
857 JS::Rooted<JS::Value> wrappedFile(aCx);
858 rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile),
859 &wrappedFile);
860 if (NS_FAILED(rv)) {
861 NS_WARNING("Failed to wrap native!");
862 return nullptr;
863 }
865 return JSVAL_TO_OBJECT(wrappedFile);
866 }
867 };
870 class CreateIndexDeserializationTraits
871 {
872 public:
873 static JSObject* CreateAndWrapFileHandle(JSContext* aCx,
874 IDBDatabase* aDatabase,
875 StructuredCloneFile& aFile,
876 const FileHandleData& aData)
877 {
878 // FileHandle can't be used in index creation, so just make a dummy object.
879 return JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr());
880 }
882 static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx,
883 IDBDatabase* aDatabase,
884 StructuredCloneFile& aFile,
885 const BlobOrFileData& aData)
886 {
887 MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
888 aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
889 aData.tag == SCTAG_DOM_BLOB);
891 // The following properties are available for use in index creation
892 // Blob.size
893 // Blob.type
894 // File.name
895 // File.lastModifiedDate
897 JS::Rooted<JSObject*> obj(aCx,
898 JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
899 if (!obj) {
900 NS_WARNING("Failed to create object!");
901 return nullptr;
902 }
904 // Technically these props go on the proto, but this detail won't change
905 // the results of index creation.
907 JS::Rooted<JSString*> type(aCx,
908 JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length()));
909 if (!type ||
910 !JS_DefineProperty(aCx, obj, "size", double(aData.size), 0) ||
911 !JS_DefineProperty(aCx, obj, "type", type, 0)) {
912 return nullptr;
913 }
915 if (aData.tag == SCTAG_DOM_BLOB) {
916 return obj;
917 }
919 JS::Rooted<JSString*> name(aCx,
920 JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length()));
921 JS::Rooted<JSObject*> date(aCx,
922 JS_NewDateObjectMsec(aCx, aData.lastModifiedDate));
923 if (!name || !date ||
924 !JS_DefineProperty(aCx, obj, "name", name, 0) ||
925 !JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0)) {
926 return nullptr;
927 }
929 return obj;
930 }
931 };
933 } // anonymous namespace
935 const JSClass IDBObjectStore::sDummyPropJSClass = {
936 "dummy", 0,
937 JS_PropertyStub, JS_DeletePropertyStub,
938 JS_PropertyStub, JS_StrictPropertyStub,
939 JS_EnumerateStub, JS_ResolveStub,
940 JS_ConvertStub
941 };
943 // static
944 already_AddRefed<IDBObjectStore>
945 IDBObjectStore::Create(IDBTransaction* aTransaction,
946 ObjectStoreInfo* aStoreInfo,
947 const nsACString& aDatabaseId,
948 bool aCreating)
949 {
950 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
952 nsRefPtr<IDBObjectStore> objectStore = new IDBObjectStore();
954 objectStore->mTransaction = aTransaction;
955 objectStore->mName = aStoreInfo->name;
956 objectStore->mId = aStoreInfo->id;
957 objectStore->mKeyPath = aStoreInfo->keyPath;
958 objectStore->mAutoIncrement = aStoreInfo->autoIncrement;
959 objectStore->mDatabaseId = aDatabaseId;
960 objectStore->mInfo = aStoreInfo;
962 if (!IndexedDatabaseManager::IsMainProcess()) {
963 IndexedDBTransactionChild* transactionActor = aTransaction->GetActorChild();
964 NS_ASSERTION(transactionActor, "Must have an actor here!");
966 ObjectStoreConstructorParams params;
968 if (aCreating) {
969 CreateObjectStoreParams createParams;
970 createParams.info() = *aStoreInfo;
971 params = createParams;
972 }
973 else {
974 GetObjectStoreParams getParams;
975 getParams.name() = aStoreInfo->name;
976 params = getParams;
977 }
979 IndexedDBObjectStoreChild* actor =
980 new IndexedDBObjectStoreChild(objectStore);
982 transactionActor->SendPIndexedDBObjectStoreConstructor(actor, params);
983 }
985 return objectStore.forget();
986 }
988 // static
989 nsresult
990 IDBObjectStore::AppendIndexUpdateInfo(
991 int64_t aIndexID,
992 const KeyPath& aKeyPath,
993 bool aUnique,
994 bool aMultiEntry,
995 JSContext* aCx,
996 JS::Handle<JS::Value> aVal,
997 nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
998 {
999 nsresult rv;
1001 if (!aMultiEntry) {
1002 Key key;
1003 rv = aKeyPath.ExtractKey(aCx, aVal, key);
1005 // If an index's keypath doesn't match an object, we ignore that object.
1006 if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) {
1007 return NS_OK;
1008 }
1010 if (NS_FAILED(rv)) {
1011 return rv;
1012 }
1014 IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1015 updateInfo->indexId = aIndexID;
1016 updateInfo->indexUnique = aUnique;
1017 updateInfo->value = key;
1019 return NS_OK;
1020 }
1022 JS::Rooted<JS::Value> val(aCx);
1023 if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
1024 return NS_OK;
1025 }
1027 if (JS_IsArrayObject(aCx, val)) {
1028 JS::Rooted<JSObject*> array(aCx, &val.toObject());
1029 uint32_t arrayLength;
1030 if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
1031 IDB_REPORT_INTERNAL_ERR();
1032 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1033 }
1035 for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
1036 JS::Rooted<JS::Value> arrayItem(aCx);
1037 if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) {
1038 IDB_REPORT_INTERNAL_ERR();
1039 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1040 }
1042 Key value;
1043 if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
1044 value.IsUnset()) {
1045 // Not a value we can do anything with, ignore it.
1046 continue;
1047 }
1049 IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1050 updateInfo->indexId = aIndexID;
1051 updateInfo->indexUnique = aUnique;
1052 updateInfo->value = value;
1053 }
1054 }
1055 else {
1056 Key value;
1057 if (NS_FAILED(value.SetFromJSVal(aCx, val)) ||
1058 value.IsUnset()) {
1059 // Not a value we can do anything with, ignore it.
1060 return NS_OK;
1061 }
1063 IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
1064 updateInfo->indexId = aIndexID;
1065 updateInfo->indexUnique = aUnique;
1066 updateInfo->value = value;
1067 }
1069 return NS_OK;
1070 }
1072 // static
1073 nsresult
1074 IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
1075 int64_t aObjectStoreId,
1076 const Key& aObjectStoreKey,
1077 bool aOverwrite,
1078 int64_t aObjectDataId,
1079 const nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1080 {
1081 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
1082 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1084 PROFILER_LABEL("IndexedDB", "IDBObjectStore::UpdateIndexes");
1086 nsresult rv;
1088 NS_ASSERTION(aObjectDataId != INT64_MIN, "Bad objectData id!");
1090 NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id");
1092 if (aOverwrite) {
1093 nsCOMPtr<mozIStorageStatement> deleteStmt =
1094 aTransaction->GetCachedStatement(
1095 "DELETE FROM unique_index_data "
1096 "WHERE object_data_id = :object_data_id; "
1097 "DELETE FROM index_data "
1098 "WHERE object_data_id = :object_data_id");
1099 NS_ENSURE_TRUE(deleteStmt, NS_ERROR_FAILURE);
1101 mozStorageStatementScoper scoper(deleteStmt);
1103 rv = deleteStmt->BindInt64ByName(objectDataId, aObjectDataId);
1104 NS_ENSURE_SUCCESS(rv, rv);
1106 rv = deleteStmt->Execute();
1107 NS_ENSURE_SUCCESS(rv, rv);
1108 }
1110 // Avoid lots of hash lookups for objectStores with lots of indexes by lazily
1111 // holding the necessary statements on the stack outside the loop.
1112 nsCOMPtr<mozIStorageStatement> insertUniqueStmt;
1113 nsCOMPtr<mozIStorageStatement> insertStmt;
1115 uint32_t infoCount = aUpdateInfoArray.Length();
1116 for (uint32_t i = 0; i < infoCount; i++) {
1117 const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i];
1119 nsCOMPtr<mozIStorageStatement>& stmt =
1120 updateInfo.indexUnique ? insertUniqueStmt : insertStmt;
1122 if (!stmt) {
1123 stmt = updateInfo.indexUnique ?
1124 aTransaction->GetCachedStatement(
1125 "INSERT INTO unique_index_data "
1126 "(index_id, object_data_id, object_data_key, value) "
1127 "VALUES (:index_id, :object_data_id, :object_data_key, :value)") :
1128 aTransaction->GetCachedStatement(
1129 "INSERT OR IGNORE INTO index_data ("
1130 "index_id, object_data_id, object_data_key, value) "
1131 "VALUES (:index_id, :object_data_id, :object_data_key, :value)");
1132 }
1133 NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE);
1135 mozStorageStatementScoper scoper(stmt);
1137 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
1138 updateInfo.indexId);
1139 NS_ENSURE_SUCCESS(rv, rv);
1141 rv = stmt->BindInt64ByName(objectDataId, aObjectDataId);
1142 NS_ENSURE_SUCCESS(rv, rv);
1144 rv = aObjectStoreKey.BindToStatement(stmt,
1145 NS_LITERAL_CSTRING("object_data_key"));
1146 NS_ENSURE_SUCCESS(rv, rv);
1148 rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value"));
1149 NS_ENSURE_SUCCESS(rv, rv);
1151 rv = stmt->Execute();
1152 if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) {
1153 // If we're inserting multiple entries for the same unique index, then
1154 // we might have failed to insert due to colliding with another entry for
1155 // the same index in which case we should ignore it.
1157 for (int32_t j = (int32_t)i - 1;
1158 j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId;
1159 --j) {
1160 if (updateInfo.value == aUpdateInfoArray[j].value) {
1161 // We found a key with the same value for the same index. So we
1162 // must have had a collision with a value we just inserted.
1163 rv = NS_OK;
1164 break;
1165 }
1166 }
1167 }
1169 if (NS_FAILED(rv)) {
1170 return rv;
1171 }
1172 }
1174 return NS_OK;
1175 }
1177 // static
1178 nsresult
1179 IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
1180 mozIStorageStatement* aStatement,
1181 uint32_t aDataIndex,
1182 uint32_t aFileIdsIndex,
1183 IDBDatabase* aDatabase,
1184 StructuredCloneReadInfo& aInfo)
1185 {
1186 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
1187 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1189 PROFILER_LABEL("IndexedDB",
1190 "IDBObjectStore::GetStructuredCloneReadInfoFromStatement");
1192 #ifdef DEBUG
1193 {
1194 int32_t type;
1195 NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) &&
1196 type == mozIStorageStatement::VALUE_TYPE_BLOB,
1197 "Bad value type!");
1198 }
1199 #endif
1201 const uint8_t* blobData;
1202 uint32_t blobDataLength;
1203 nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength,
1204 &blobData);
1205 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1207 const char* compressed = reinterpret_cast<const char*>(blobData);
1208 size_t compressedLength = size_t(blobDataLength);
1210 static const fallible_t fallible = fallible_t();
1212 size_t uncompressedLength;
1213 if (!snappy::GetUncompressedLength(compressed, compressedLength,
1214 &uncompressedLength)) {
1215 IDB_WARNING("Snappy can't determine uncompressed length!");
1216 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1217 }
1219 nsAutoArrayPtr<char> uncompressed(new (fallible) char[uncompressedLength]);
1220 NS_ENSURE_TRUE(uncompressed, NS_ERROR_OUT_OF_MEMORY);
1222 if (!snappy::RawUncompress(compressed, compressedLength,
1223 uncompressed.get())) {
1224 IDB_WARNING("Snappy can't determine uncompressed length!");
1225 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1226 }
1228 JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer;
1229 if (!buffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
1230 uncompressedLength)) {
1231 IDB_REPORT_INTERNAL_ERR();
1232 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1233 }
1235 bool isNull;
1236 rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
1237 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1239 if (!isNull) {
1240 nsString ids;
1241 rv = aStatement->GetString(aFileIdsIndex, ids);
1242 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1244 nsAutoTArray<int64_t, 10> array;
1245 rv = ConvertFileIdsToArray(ids, array);
1246 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1248 FileManager* fileManager = aDatabase->Manager();
1250 for (uint32_t i = 0; i < array.Length(); i++) {
1251 const int64_t& id = array[i];
1253 nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(id);
1254 NS_ASSERTION(fileInfo, "Null file info!");
1256 StructuredCloneFile* file = aInfo.mFiles.AppendElement();
1257 file->mFileInfo.swap(fileInfo);
1258 }
1259 }
1261 aInfo.mDatabase = aDatabase;
1263 return NS_OK;
1264 }
1266 // static
1267 void
1268 IDBObjectStore::ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo)
1269 {
1270 // This is kind of tricky, we only want to release stuff on the main thread,
1271 // but we can end up being called on other threads if we have already been
1272 // cleared on the main thread.
1273 if (!aWriteInfo.mCloneBuffer.data() && !aWriteInfo.mFiles.Length()) {
1274 return;
1275 }
1277 // If there's something to clear, we should be on the main thread.
1278 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1280 ClearStructuredCloneBuffer(aWriteInfo.mCloneBuffer);
1281 aWriteInfo.mFiles.Clear();
1282 }
1284 // static
1285 void
1286 IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
1287 {
1288 // This is kind of tricky, we only want to release stuff on the main thread,
1289 // but we can end up being called on other threads if we have already been
1290 // cleared on the main thread.
1291 if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
1292 return;
1293 }
1295 // If there's something to clear, we should be on the main thread.
1296 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1298 ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer);
1299 aReadInfo.mFiles.Clear();
1300 }
1302 // static
1303 void
1304 IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
1305 {
1306 if (aBuffer.data()) {
1307 aBuffer.clear();
1308 }
1309 }
1311 // static
1312 bool
1313 IDBObjectStore::DeserializeValue(JSContext* aCx,
1314 StructuredCloneReadInfo& aCloneReadInfo,
1315 JS::MutableHandle<JS::Value> aValue)
1316 {
1317 NS_ASSERTION(NS_IsMainThread(),
1318 "Should only be deserializing on the main thread!");
1319 NS_ASSERTION(aCx, "A JSContext is required!");
1321 JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer;
1323 if (!buffer.data()) {
1324 aValue.setUndefined();
1325 return true;
1326 }
1328 JSAutoRequest ar(aCx);
1330 JSStructuredCloneCallbacks callbacks = {
1331 IDBObjectStore::StructuredCloneReadCallback<MainThreadDeserializationTraits>,
1332 nullptr,
1333 nullptr,
1334 nullptr,
1335 nullptr,
1336 nullptr
1337 };
1339 return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
1340 }
1342 // static
1343 bool
1344 IDBObjectStore::SerializeValue(JSContext* aCx,
1345 StructuredCloneWriteInfo& aCloneWriteInfo,
1346 JS::Handle<JS::Value> aValue)
1347 {
1348 NS_ASSERTION(NS_IsMainThread(),
1349 "Should only be serializing on the main thread!");
1350 NS_ASSERTION(aCx, "A JSContext is required!");
1352 JSAutoRequest ar(aCx);
1354 JSStructuredCloneCallbacks callbacks = {
1355 nullptr,
1356 StructuredCloneWriteCallback,
1357 nullptr,
1358 nullptr,
1359 nullptr,
1360 nullptr
1361 };
1363 JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer;
1365 return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo);
1366 }
1368 static inline bool
1369 StructuredCloneReadString(JSStructuredCloneReader* aReader,
1370 nsCString& aString)
1371 {
1372 uint32_t length;
1373 if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
1374 NS_WARNING("Failed to read length!");
1375 return false;
1376 }
1377 length = NativeEndian::swapFromLittleEndian(length);
1379 if (!aString.SetLength(length, fallible_t())) {
1380 NS_WARNING("Out of memory?");
1381 return false;
1382 }
1383 char* buffer = aString.BeginWriting();
1385 if (!JS_ReadBytes(aReader, buffer, length)) {
1386 NS_WARNING("Failed to read type!");
1387 return false;
1388 }
1390 return true;
1391 }
1393 // static
1394 bool
1395 IDBObjectStore::ReadFileHandle(JSStructuredCloneReader* aReader,
1396 FileHandleData* aRetval)
1397 {
1398 static_assert(SCTAG_DOM_FILEHANDLE == 0xFFFF8004,
1399 "Update me!");
1400 MOZ_ASSERT(aReader && aRetval);
1402 nsCString type;
1403 if (!StructuredCloneReadString(aReader, type)) {
1404 return false;
1405 }
1406 CopyUTF8toUTF16(type, aRetval->type);
1408 nsCString name;
1409 if (!StructuredCloneReadString(aReader, name)) {
1410 return false;
1411 }
1412 CopyUTF8toUTF16(name, aRetval->name);
1414 return true;
1415 }
1417 // static
1418 bool
1419 IDBObjectStore::ReadBlobOrFile(JSStructuredCloneReader* aReader,
1420 uint32_t aTag,
1421 BlobOrFileData* aRetval)
1422 {
1423 static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 &&
1424 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 &&
1425 SCTAG_DOM_FILE == 0xFFFF8005,
1426 "Update me!");
1427 MOZ_ASSERT(aReader && aRetval);
1428 MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
1429 aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
1430 aTag == SCTAG_DOM_BLOB);
1432 aRetval->tag = aTag;
1434 // If it's not a FileHandle, it's a Blob or a File.
1435 uint64_t size;
1436 if (!JS_ReadBytes(aReader, &size, sizeof(uint64_t))) {
1437 NS_WARNING("Failed to read size!");
1438 return false;
1439 }
1440 aRetval->size = NativeEndian::swapFromLittleEndian(size);
1442 nsCString type;
1443 if (!StructuredCloneReadString(aReader, type)) {
1444 return false;
1445 }
1446 CopyUTF8toUTF16(type, aRetval->type);
1448 // Blobs are done.
1449 if (aTag == SCTAG_DOM_BLOB) {
1450 return true;
1451 }
1453 NS_ASSERTION(aTag == SCTAG_DOM_FILE ||
1454 aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE, "Huh?!");
1456 uint64_t lastModifiedDate;
1457 if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
1458 lastModifiedDate = UINT64_MAX;
1459 }
1460 else {
1461 if(!JS_ReadBytes(aReader, &lastModifiedDate, sizeof(lastModifiedDate))) {
1462 NS_WARNING("Failed to read lastModifiedDate");
1463 return false;
1464 }
1465 lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
1466 }
1467 aRetval->lastModifiedDate = lastModifiedDate;
1469 nsCString name;
1470 if (!StructuredCloneReadString(aReader, name)) {
1471 return false;
1472 }
1473 CopyUTF8toUTF16(name, aRetval->name);
1475 return true;
1476 }
1478 // static
1479 template <class DeserializationTraits>
1480 JSObject*
1481 IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
1482 JSStructuredCloneReader* aReader,
1483 uint32_t aTag,
1484 uint32_t aData,
1485 void* aClosure)
1486 {
1487 // We need to statically assert that our tag values are what we expect
1488 // so that if people accidentally change them they notice.
1489 static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 &&
1490 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 &&
1491 SCTAG_DOM_FILEHANDLE == 0xFFFF8004 &&
1492 SCTAG_DOM_FILE == 0xFFFF8005,
1493 "You changed our structured clone tag values and just ate "
1494 "everyone's IndexedDB data. I hope you are happy.");
1496 if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
1497 aTag == SCTAG_DOM_FILEHANDLE ||
1498 aTag == SCTAG_DOM_BLOB ||
1499 aTag == SCTAG_DOM_FILE) {
1500 StructuredCloneReadInfo* cloneReadInfo =
1501 reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
1503 if (aData >= cloneReadInfo->mFiles.Length()) {
1504 NS_ERROR("Bad blob index!");
1505 return nullptr;
1506 }
1508 StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
1509 IDBDatabase* database = cloneReadInfo->mDatabase;
1511 if (aTag == SCTAG_DOM_FILEHANDLE) {
1512 FileHandleData data;
1513 if (!ReadFileHandle(aReader, &data)) {
1514 return nullptr;
1515 }
1517 return DeserializationTraits::CreateAndWrapFileHandle(aCx, database,
1518 file, data);
1519 }
1521 BlobOrFileData data;
1522 if (!ReadBlobOrFile(aReader, aTag, &data)) {
1523 return nullptr;
1524 }
1526 return DeserializationTraits::CreateAndWrapBlobOrFile(aCx, database,
1527 file, data);
1528 }
1530 const JSStructuredCloneCallbacks* runtimeCallbacks =
1531 js::GetContextStructuredCloneCallbacks(aCx);
1533 if (runtimeCallbacks) {
1534 return runtimeCallbacks->read(aCx, aReader, aTag, aData, nullptr);
1535 }
1537 return nullptr;
1538 }
1540 // static
1541 bool
1542 IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
1543 JSStructuredCloneWriter* aWriter,
1544 JS::Handle<JSObject*> aObj,
1545 void* aClosure)
1546 {
1547 StructuredCloneWriteInfo* cloneWriteInfo =
1548 reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
1550 if (JS_GetClass(aObj) == &sDummyPropJSClass) {
1551 NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0,
1552 "We should not have been here before!");
1553 cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter);
1555 uint64_t value = 0;
1556 // Omit endian swap
1557 return JS_WriteBytes(aWriter, &value, sizeof(value));
1558 }
1560 IDBTransaction* transaction = cloneWriteInfo->mTransaction;
1561 FileManager* fileManager = transaction->Database()->Manager();
1563 file::FileHandle* fileHandle = nullptr;
1564 if (NS_SUCCEEDED(UNWRAP_OBJECT(FileHandle, aObj, fileHandle))) {
1565 nsRefPtr<FileInfo> fileInfo = fileHandle->GetFileInfo();
1567 // Throw when trying to store non IDB file handles or IDB file handles
1568 // across databases.
1569 if (!fileInfo || fileInfo->Manager() != fileManager) {
1570 return false;
1571 }
1573 NS_ConvertUTF16toUTF8 convType(fileHandle->Type());
1574 uint32_t convTypeLength =
1575 NativeEndian::swapToLittleEndian(convType.Length());
1577 NS_ConvertUTF16toUTF8 convName(fileHandle->Name());
1578 uint32_t convNameLength =
1579 NativeEndian::swapToLittleEndian(convName.Length());
1581 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILEHANDLE,
1582 cloneWriteInfo->mFiles.Length()) ||
1583 !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
1584 !JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
1585 !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
1586 !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
1587 return false;
1588 }
1590 StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement();
1591 file->mFileInfo = fileInfo.forget();
1593 return true;
1594 }
1596 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
1597 nsContentUtils::XPConnect()->
1598 GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
1600 if (wrappedNative) {
1601 nsISupports* supports = wrappedNative->Native();
1603 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
1604 if (blob) {
1605 nsCOMPtr<nsIInputStream> inputStream;
1607 // Check if it is a blob created from this db or the blob was already
1608 // stored in this db
1609 nsRefPtr<FileInfo> fileInfo = transaction->GetFileInfo(blob);
1610 if (!fileInfo && fileManager) {
1611 fileInfo = blob->GetFileInfo(fileManager);
1613 if (!fileInfo) {
1614 fileInfo = fileManager->GetNewFileInfo();
1615 if (!fileInfo) {
1616 NS_WARNING("Failed to get new file info!");
1617 return false;
1618 }
1620 if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
1621 NS_WARNING("Failed to get internal steam!");
1622 return false;
1623 }
1625 transaction->AddFileInfo(blob, fileInfo);
1626 }
1627 }
1629 uint64_t size;
1630 if (NS_FAILED(blob->GetSize(&size))) {
1631 NS_WARNING("Failed to get size!");
1632 return false;
1633 }
1634 size = NativeEndian::swapToLittleEndian(size);
1636 nsString type;
1637 if (NS_FAILED(blob->GetType(type))) {
1638 NS_WARNING("Failed to get type!");
1639 return false;
1640 }
1641 NS_ConvertUTF16toUTF8 convType(type);
1642 uint32_t convTypeLength =
1643 NativeEndian::swapToLittleEndian(convType.Length());
1645 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
1647 if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
1648 cloneWriteInfo->mFiles.Length()) ||
1649 !JS_WriteBytes(aWriter, &size, sizeof(size)) ||
1650 !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
1651 !JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
1652 return false;
1653 }
1655 if (file) {
1656 uint64_t lastModifiedDate = 0;
1657 if (NS_FAILED(file->GetMozLastModifiedDate(&lastModifiedDate))) {
1658 NS_WARNING("Failed to get last modified date!");
1659 return false;
1660 }
1662 lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate);
1664 nsString name;
1665 if (NS_FAILED(file->GetName(name))) {
1666 NS_WARNING("Failed to get name!");
1667 return false;
1668 }
1669 NS_ConvertUTF16toUTF8 convName(name);
1670 uint32_t convNameLength =
1671 NativeEndian::swapToLittleEndian(convName.Length());
1673 if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
1674 !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
1675 !JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
1676 return false;
1677 }
1678 }
1680 StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement();
1681 cloneFile->mFile = blob.forget();
1682 cloneFile->mFileInfo = fileInfo.forget();
1683 cloneFile->mInputStream = inputStream.forget();
1685 return true;
1686 }
1687 }
1689 // try using the runtime callbacks
1690 const JSStructuredCloneCallbacks* runtimeCallbacks =
1691 js::GetContextStructuredCloneCallbacks(aCx);
1692 if (runtimeCallbacks) {
1693 return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr);
1694 }
1696 return false;
1697 }
1699 // static
1700 nsresult
1701 IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
1702 nsTArray<int64_t>& aResult)
1703 {
1704 nsCharSeparatedTokenizerTemplate<IgnoreNothing> tokenizer(aFileIds, ' ');
1706 while (tokenizer.hasMoreTokens()) {
1707 nsString token(tokenizer.nextToken());
1709 NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!");
1711 nsresult rv;
1712 int32_t id = token.ToInteger(&rv);
1713 NS_ENSURE_SUCCESS(rv, rv);
1715 int64_t* element = aResult.AppendElement();
1716 *element = id;
1717 }
1719 return NS_OK;
1720 }
1722 // static
1723 void
1724 IDBObjectStore::ConvertActorsToBlobs(
1725 const InfallibleTArray<PBlobChild*>& aActors,
1726 nsTArray<StructuredCloneFile>& aFiles)
1727 {
1728 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1729 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1730 NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!");
1732 if (!aActors.IsEmpty()) {
1733 NS_ASSERTION(ContentChild::GetSingleton(), "This should never be null!");
1735 uint32_t length = aActors.Length();
1736 aFiles.SetCapacity(length);
1738 for (uint32_t index = 0; index < length; index++) {
1739 BlobChild* actor = static_cast<BlobChild*>(aActors[index]);
1741 StructuredCloneFile* file = aFiles.AppendElement();
1742 file->mFile = actor->GetBlob();
1743 }
1744 }
1745 }
1747 // static
1748 nsresult
1749 IDBObjectStore::ConvertBlobsToActors(
1750 ContentParent* aContentParent,
1751 FileManager* aFileManager,
1752 const nsTArray<StructuredCloneFile>& aFiles,
1753 InfallibleTArray<PBlobParent*>& aActors)
1754 {
1755 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1756 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1757 NS_ASSERTION(aContentParent, "Null contentParent!");
1758 NS_ASSERTION(aFileManager, "Null file manager!");
1760 if (!aFiles.IsEmpty()) {
1761 nsCOMPtr<nsIFile> directory = aFileManager->GetDirectory();
1762 if (!directory) {
1763 IDB_WARNING("Failed to get directory!");
1764 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1765 }
1767 uint32_t fileCount = aFiles.Length();
1768 aActors.SetCapacity(fileCount);
1770 for (uint32_t index = 0; index < fileCount; index++) {
1771 const StructuredCloneFile& file = aFiles[index];
1772 NS_ASSERTION(file.mFileInfo, "This should never be null!");
1774 nsCOMPtr<nsIFile> nativeFile =
1775 aFileManager->GetFileForId(directory, file.mFileInfo->Id());
1776 if (!nativeFile) {
1777 IDB_WARNING("Failed to get file!");
1778 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1779 }
1781 nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(nativeFile, file.mFileInfo);
1783 BlobParent* actor =
1784 aContentParent->GetOrCreateActorForBlob(blob);
1785 if (!actor) {
1786 // This can only fail if the child has crashed.
1787 IDB_REPORT_INTERNAL_ERR();
1788 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1789 }
1791 aActors.AppendElement(actor);
1792 }
1793 }
1795 return NS_OK;
1796 }
1798 IDBObjectStore::IDBObjectStore()
1799 : mId(INT64_MIN),
1800 mKeyPath(0),
1801 mCachedKeyPath(JSVAL_VOID),
1802 mRooted(false),
1803 mAutoIncrement(false),
1804 mActorChild(nullptr),
1805 mActorParent(nullptr)
1806 {
1807 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1809 SetIsDOMBinding();
1810 }
1812 IDBObjectStore::~IDBObjectStore()
1813 {
1814 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1815 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
1816 if (mActorChild) {
1817 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1818 mActorChild->Send__delete__(mActorChild);
1819 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
1820 }
1822 if (mRooted) {
1823 mCachedKeyPath = JSVAL_VOID;
1824 mozilla::DropJSObjects(this);
1825 }
1826 }
1828 nsresult
1829 IDBObjectStore::GetAddInfo(JSContext* aCx,
1830 JS::Handle<JS::Value> aValue,
1831 JS::Handle<JS::Value> aKeyVal,
1832 StructuredCloneWriteInfo& aCloneWriteInfo,
1833 Key& aKey,
1834 nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
1835 {
1836 nsresult rv;
1838 // Return DATA_ERR if a key was passed in and this objectStore uses inline
1839 // keys.
1840 if (!JSVAL_IS_VOID(aKeyVal) && HasValidKeyPath()) {
1841 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1842 }
1844 JSAutoRequest ar(aCx);
1846 if (!HasValidKeyPath()) {
1847 // Out-of-line keys must be passed in.
1848 rv = aKey.SetFromJSVal(aCx, aKeyVal);
1849 if (NS_FAILED(rv)) {
1850 return rv;
1851 }
1852 }
1853 else if (!mAutoIncrement) {
1854 rv = GetKeyPath().ExtractKey(aCx, aValue, aKey);
1855 if (NS_FAILED(rv)) {
1856 return rv;
1857 }
1858 }
1860 // Return DATA_ERR if no key was specified this isn't an autoIncrement
1861 // objectStore.
1862 if (aKey.IsUnset() && !mAutoIncrement) {
1863 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
1864 }
1866 // Figure out indexes and the index values to update here.
1867 uint32_t count = mInfo->indexes.Length();
1868 aUpdateInfoArray.SetCapacity(count); // Pretty good estimate
1869 for (uint32_t indexesIndex = 0; indexesIndex < count; indexesIndex++) {
1870 const IndexInfo& indexInfo = mInfo->indexes[indexesIndex];
1872 rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath,
1873 indexInfo.unique, indexInfo.multiEntry, aCx,
1874 aValue, aUpdateInfoArray);
1875 NS_ENSURE_SUCCESS(rv, rv);
1876 }
1878 GetAddInfoClosure data = {this, aCloneWriteInfo, aValue};
1880 if (mAutoIncrement && HasValidKeyPath()) {
1881 NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!");
1883 rv = GetKeyPath().ExtractOrCreateKey(aCx, aValue, aKey,
1884 &GetAddInfoCallback, &data);
1885 }
1886 else {
1887 rv = GetAddInfoCallback(aCx, &data);
1888 }
1890 return rv;
1891 }
1893 already_AddRefed<IDBRequest>
1894 IDBObjectStore::AddOrPut(JSContext* aCx, JS::Handle<JS::Value> aValue,
1895 JS::Handle<JS::Value> aKey,
1896 bool aOverwrite, ErrorResult& aRv)
1897 {
1898 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1900 if (!mTransaction->IsOpen()) {
1901 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
1902 return nullptr;
1903 }
1905 if (!IsWriteAllowed()) {
1906 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
1907 return nullptr;
1908 }
1910 StructuredCloneWriteInfo cloneWriteInfo;
1911 Key key;
1912 nsTArray<IndexUpdateInfo> updateInfo;
1914 JS::Rooted<JS::Value> value(aCx, aValue);
1915 aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo);
1916 if (aRv.Failed()) {
1917 return nullptr;
1918 }
1920 nsRefPtr<IDBRequest> request = GenerateRequest(this);
1921 if (!request) {
1922 IDB_WARNING("Failed to generate request!");
1923 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1924 return nullptr;
1925 }
1927 nsRefPtr<AddHelper> helper =
1928 new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key,
1929 aOverwrite, updateInfo);
1931 nsresult rv = helper->DispatchToTransactionPool();
1932 if (NS_FAILED(rv)) {
1933 IDB_WARNING("Failed to dispatch!");
1934 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1935 return nullptr;
1936 }
1938 #ifdef IDB_PROFILER_USE_MARKS
1939 if (aOverwrite) {
1940 IDB_PROFILER_MARK("IndexedDB Request %llu: "
1941 "database(%s).transaction(%s).objectStore(%s).%s(%s)",
1942 "IDBRequest[%llu] MT IDBObjectStore.put()",
1943 request->GetSerialNumber(),
1944 IDB_PROFILER_STRING(Transaction()->Database()),
1945 IDB_PROFILER_STRING(Transaction()),
1946 IDB_PROFILER_STRING(this),
1947 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
1948 }
1949 else {
1950 IDB_PROFILER_MARK("IndexedDB Request %llu: "
1951 "database(%s).transaction(%s).objectStore(%s).add(%s)",
1952 "IDBRequest[%llu] MT IDBObjectStore.add()",
1953 request->GetSerialNumber(),
1954 IDB_PROFILER_STRING(Transaction()->Database()),
1955 IDB_PROFILER_STRING(Transaction()),
1956 IDB_PROFILER_STRING(this),
1957 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
1958 }
1959 #endif
1961 return request.forget();
1962 }
1964 nsresult
1965 IDBObjectStore::AddOrPutInternal(
1966 const SerializedStructuredCloneWriteInfo& aCloneWriteInfo,
1967 const Key& aKey,
1968 const InfallibleTArray<IndexUpdateInfo>& aUpdateInfoArray,
1969 const nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
1970 bool aOverwrite,
1971 IDBRequest** _retval)
1972 {
1973 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1974 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
1976 if (!mTransaction->IsOpen()) {
1977 return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
1978 }
1980 if (!IsWriteAllowed()) {
1981 return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
1982 }
1984 nsRefPtr<IDBRequest> request = GenerateRequest(this);
1985 IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1987 StructuredCloneWriteInfo cloneWriteInfo;
1988 if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) {
1989 IDB_WARNING("Failed to copy structured clone buffer!");
1990 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1991 }
1993 if (!aBlobs.IsEmpty()) {
1994 FileManager* fileManager = Transaction()->Database()->Manager();
1995 NS_ASSERTION(fileManager, "Null file manager?!");
1997 uint32_t length = aBlobs.Length();
1998 cloneWriteInfo.mFiles.SetCapacity(length);
2000 for (uint32_t index = 0; index < length; index++) {
2001 const nsCOMPtr<nsIDOMBlob>& blob = aBlobs[index];
2003 nsCOMPtr<nsIInputStream> inputStream;
2005 nsRefPtr<FileInfo> fileInfo = Transaction()->GetFileInfo(blob);
2006 if (!fileInfo) {
2007 fileInfo = blob->GetFileInfo(fileManager);
2009 if (!fileInfo) {
2010 fileInfo = fileManager->GetNewFileInfo();
2011 if (!fileInfo) {
2012 IDB_WARNING("Failed to get new file info!");
2013 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2014 }
2016 if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
2017 IDB_WARNING("Failed to get internal steam!");
2018 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2019 }
2021 // XXXbent This is where we should send a message back to the child to
2022 // update the file id.
2024 Transaction()->AddFileInfo(blob, fileInfo);
2025 }
2026 }
2028 StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement();
2029 file->mFile = blob;
2030 file->mFileInfo.swap(fileInfo);
2031 file->mInputStream.swap(inputStream);
2032 }
2033 }
2035 Key key(aKey);
2037 nsTArray<IndexUpdateInfo> updateInfo(aUpdateInfoArray);
2039 nsRefPtr<AddHelper> helper =
2040 new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key,
2041 aOverwrite, updateInfo);
2043 nsresult rv = helper->DispatchToTransactionPool();
2044 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2046 #ifdef IDB_PROFILER_USE_MARKS
2047 if (aOverwrite) {
2048 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2049 "database(%s).transaction(%s).objectStore(%s).%s(%s)",
2050 "IDBRequest[%llu] MT IDBObjectStore.put()",
2051 request->GetSerialNumber(),
2052 IDB_PROFILER_STRING(Transaction()->Database()),
2053 IDB_PROFILER_STRING(Transaction()),
2054 IDB_PROFILER_STRING(this),
2055 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
2056 }
2057 else {
2058 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2059 "database(%s).transaction(%s).objectStore(%s).add(%s)",
2060 "IDBRequest[%llu] MT IDBObjectStore.add()",
2061 request->GetSerialNumber(),
2062 IDB_PROFILER_STRING(Transaction()->Database()),
2063 IDB_PROFILER_STRING(Transaction()),
2064 IDB_PROFILER_STRING(this),
2065 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
2066 }
2067 #endif
2069 request.forget(_retval);
2070 return NS_OK;
2071 }
2073 already_AddRefed<IDBRequest>
2074 IDBObjectStore::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv)
2075 {
2076 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2077 NS_ASSERTION(aKeyRange, "Null pointer!");
2079 if (!mTransaction->IsOpen()) {
2080 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2081 return nullptr;
2082 }
2084 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2085 if (!request) {
2086 IDB_WARNING("Failed to generate request!");
2087 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2088 return nullptr;
2089 }
2091 nsRefPtr<GetHelper> helper =
2092 new GetHelper(mTransaction, request, this, aKeyRange);
2094 nsresult rv = helper->DispatchToTransactionPool();
2095 if (NS_FAILED(rv)) {
2096 IDB_WARNING("Failed to dispatch!");
2097 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2098 return nullptr;
2099 }
2101 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2102 "database(%s).transaction(%s).objectStore(%s).get(%s)",
2103 "IDBRequest[%llu] MT IDBObjectStore.get()",
2104 request->GetSerialNumber(),
2105 IDB_PROFILER_STRING(Transaction()->Database()),
2106 IDB_PROFILER_STRING(Transaction()),
2107 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
2109 return request.forget();
2110 }
2112 already_AddRefed<IDBRequest>
2113 IDBObjectStore::GetAllInternal(IDBKeyRange* aKeyRange,
2114 uint32_t aLimit, ErrorResult& aRv)
2115 {
2116 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2118 if (!mTransaction->IsOpen()) {
2119 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2120 return nullptr;
2121 }
2123 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2124 if (!request) {
2125 IDB_WARNING("Failed to generate request!");
2126 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2127 return nullptr;
2128 }
2130 nsRefPtr<GetAllHelper> helper =
2131 new GetAllHelper(mTransaction, request, this, aKeyRange, aLimit);
2133 nsresult rv = helper->DispatchToTransactionPool();
2134 if (NS_FAILED(rv)) {
2135 IDB_WARNING("Failed to dispatch!");
2136 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2137 return nullptr;
2138 }
2140 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2141 "database(%s).transaction(%s).objectStore(%s)."
2142 "getAll(%s, %lu)",
2143 "IDBRequest[%llu] MT IDBObjectStore.getAll()",
2144 request->GetSerialNumber(),
2145 IDB_PROFILER_STRING(Transaction()->Database()),
2146 IDB_PROFILER_STRING(Transaction()),
2147 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2148 aLimit);
2150 return request.forget();
2151 }
2153 already_AddRefed<IDBRequest>
2154 IDBObjectStore::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit,
2155 ErrorResult& aRv)
2156 {
2157 MOZ_ASSERT(NS_IsMainThread());
2159 if (!mTransaction->IsOpen()) {
2160 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2161 return nullptr;
2162 }
2164 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2165 if (!request) {
2166 IDB_WARNING("Failed to generate request!");
2167 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2168 return nullptr;
2169 }
2171 nsRefPtr<GetAllKeysHelper> helper =
2172 new GetAllKeysHelper(mTransaction, request, this, aKeyRange, aLimit);
2174 nsresult rv = helper->DispatchToTransactionPool();
2175 if (NS_FAILED(rv)) {
2176 IDB_WARNING("Failed to dispatch!");
2177 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2178 return nullptr;
2179 }
2181 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2182 "database(%s).transaction(%s).objectStore(%s)."
2183 "getAllKeys(%s, %lu)",
2184 "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()",
2185 request->GetSerialNumber(),
2186 IDB_PROFILER_STRING(Transaction()->Database()),
2187 IDB_PROFILER_STRING(Transaction()),
2188 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2189 aLimit);
2191 return request.forget();
2192 }
2194 already_AddRefed<IDBRequest>
2195 IDBObjectStore::DeleteInternal(IDBKeyRange* aKeyRange,
2196 ErrorResult& aRv)
2197 {
2198 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2199 NS_ASSERTION(aKeyRange, "Null key range!");
2201 if (!mTransaction->IsOpen()) {
2202 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2203 return nullptr;
2204 }
2206 if (!IsWriteAllowed()) {
2207 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2208 return nullptr;
2209 }
2211 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2212 if (!request) {
2213 IDB_WARNING("Failed to generate request!");
2214 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2215 return nullptr;
2216 }
2218 nsRefPtr<DeleteHelper> helper =
2219 new DeleteHelper(mTransaction, request, this, aKeyRange);
2221 nsresult rv = helper->DispatchToTransactionPool();
2222 if (NS_FAILED(rv)) {
2223 IDB_WARNING("Failed to dispatch!");
2224 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2225 return nullptr;
2226 }
2228 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2229 "database(%s).transaction(%s).objectStore(%s).delete(%s)",
2230 "IDBRequest[%llu] MT IDBObjectStore.delete()",
2231 request->GetSerialNumber(),
2232 IDB_PROFILER_STRING(Transaction()->Database()),
2233 IDB_PROFILER_STRING(Transaction()),
2234 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
2236 return request.forget();
2237 }
2239 already_AddRefed<IDBRequest>
2240 IDBObjectStore::Clear(ErrorResult& aRv)
2241 {
2242 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2244 if (!mTransaction->IsOpen()) {
2245 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2246 return nullptr;
2247 }
2249 if (!IsWriteAllowed()) {
2250 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2251 return nullptr;
2252 }
2254 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2255 if (!request) {
2256 IDB_WARNING("Failed to generate request!");
2257 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2258 return nullptr;
2259 }
2261 nsRefPtr<ClearHelper> helper(new ClearHelper(mTransaction, request, this));
2263 nsresult rv = helper->DispatchToTransactionPool();
2264 if (NS_FAILED(rv)) {
2265 IDB_WARNING("Failed to dispatch!");
2266 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2267 return nullptr;
2268 }
2270 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2271 "database(%s).transaction(%s).objectStore(%s).clear()",
2272 "IDBRequest[%llu] MT IDBObjectStore.clear()",
2273 request->GetSerialNumber(),
2274 IDB_PROFILER_STRING(Transaction()->Database()),
2275 IDB_PROFILER_STRING(Transaction()),
2276 IDB_PROFILER_STRING(this));
2278 return request.forget();
2279 }
2281 already_AddRefed<IDBRequest>
2282 IDBObjectStore::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv)
2283 {
2284 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2286 if (!mTransaction->IsOpen()) {
2287 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2288 return nullptr;
2289 }
2291 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2292 if (!request) {
2293 IDB_WARNING("Failed to generate request!");
2294 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2295 return nullptr;
2296 }
2298 nsRefPtr<CountHelper> helper =
2299 new CountHelper(mTransaction, request, this, aKeyRange);
2300 nsresult rv = helper->DispatchToTransactionPool();
2301 if (NS_FAILED(rv)) {
2302 IDB_WARNING("Failed to dispatch!");
2303 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2304 return nullptr;
2305 }
2307 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2308 "database(%s).transaction(%s).objectStore(%s).count(%s)",
2309 "IDBRequest[%llu] MT IDBObjectStore.count()",
2310 request->GetSerialNumber(),
2311 IDB_PROFILER_STRING(Transaction()->Database()),
2312 IDB_PROFILER_STRING(Transaction()),
2313 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
2315 return request.forget();
2316 }
2318 already_AddRefed<IDBRequest>
2319 IDBObjectStore::OpenCursorInternal(IDBKeyRange* aKeyRange,
2320 size_t aDirection, ErrorResult& aRv)
2321 {
2322 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2324 if (!mTransaction->IsOpen()) {
2325 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2326 return nullptr;
2327 }
2329 IDBCursor::Direction direction =
2330 static_cast<IDBCursor::Direction>(aDirection);
2332 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2333 if (!request) {
2334 IDB_WARNING("Failed to generate request!");
2335 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2336 return nullptr;
2337 }
2339 nsRefPtr<OpenCursorHelper> helper =
2340 new OpenCursorHelper(mTransaction, request, this, aKeyRange, direction);
2342 nsresult rv = helper->DispatchToTransactionPool();
2343 if (NS_FAILED(rv)) {
2344 IDB_WARNING("Failed to dispatch!");
2345 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2346 return nullptr;
2347 }
2349 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2350 "database(%s).transaction(%s).objectStore(%s)."
2351 "openCursor(%s, %s)",
2352 "IDBRequest[%llu] MT IDBObjectStore.openCursor()",
2353 request->GetSerialNumber(),
2354 IDB_PROFILER_STRING(Transaction()->Database()),
2355 IDB_PROFILER_STRING(Transaction()),
2356 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2357 IDB_PROFILER_STRING(direction));
2359 return request.forget();
2360 }
2362 nsresult
2363 IDBObjectStore::OpenCursorFromChildProcess(
2364 IDBRequest* aRequest,
2365 size_t aDirection,
2366 const Key& aKey,
2367 const SerializedStructuredCloneReadInfo& aCloneInfo,
2368 nsTArray<StructuredCloneFile>& aBlobs,
2369 IDBCursor** _retval)
2370 {
2371 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2372 NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) ||
2373 (aCloneInfo.dataLength && aCloneInfo.data),
2374 "Inconsistent clone info!");
2376 IDBCursor::Direction direction =
2377 static_cast<IDBCursor::Direction>(aDirection);
2379 StructuredCloneReadInfo cloneInfo;
2381 if (!cloneInfo.SetFromSerialized(aCloneInfo)) {
2382 IDB_WARNING("Failed to copy clone buffer!");
2383 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2384 }
2386 cloneInfo.mFiles.SwapElements(aBlobs);
2388 nsRefPtr<IDBCursor> cursor =
2389 IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
2390 EmptyCString(), EmptyCString(), aKey, Move(cloneInfo));
2391 IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2393 NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!");
2395 cursor.forget(_retval);
2396 return NS_OK;
2397 }
2399 nsresult
2400 IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest,
2401 size_t aDirection,
2402 const Key& aKey,
2403 IDBCursor** _retval)
2404 {
2405 MOZ_ASSERT(NS_IsMainThread());
2406 MOZ_ASSERT(aRequest);
2408 auto direction = static_cast<IDBCursor::Direction>(aDirection);
2410 nsRefPtr<IDBCursor> cursor =
2411 IDBCursor::Create(aRequest, mTransaction, this, direction, Key(),
2412 EmptyCString(), EmptyCString(), aKey);
2413 IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2415 cursor.forget(_retval);
2416 return NS_OK;
2417 }
2419 already_AddRefed<IDBRequest>
2420 IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection,
2421 ErrorResult& aRv)
2422 {
2423 MOZ_ASSERT(NS_IsMainThread());
2425 if (!mTransaction->IsOpen()) {
2426 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2427 return nullptr;
2428 }
2430 nsRefPtr<IDBRequest> request = GenerateRequest(this);
2431 if (!request) {
2432 IDB_WARNING("Failed to generate request!");
2433 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2434 return nullptr;
2435 }
2437 auto direction = static_cast<IDBCursor::Direction>(aDirection);
2439 nsRefPtr<OpenKeyCursorHelper> helper =
2440 new OpenKeyCursorHelper(mTransaction, request, this, aKeyRange, direction);
2442 nsresult rv = helper->DispatchToTransactionPool();
2443 if (NS_FAILED(rv)) {
2444 IDB_WARNING("Failed to dispatch!");
2445 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2446 return nullptr;
2447 }
2449 IDB_PROFILER_MARK("IndexedDB Request %llu: "
2450 "database(%s).transaction(%s).objectStore(%s)."
2451 "openKeyCursor(%s, %s)",
2452 "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()",
2453 request->GetSerialNumber(),
2454 IDB_PROFILER_STRING(Transaction()->Database()),
2455 IDB_PROFILER_STRING(Transaction()),
2456 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
2457 IDB_PROFILER_STRING(direction));
2459 return request.forget();
2460 }
2462 void
2463 IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo)
2464 {
2465 NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
2466 NS_ASSERTION(aInfo != mInfo, "This is nonsense");
2468 mInfo = aInfo;
2469 }
2471 already_AddRefed<IDBIndex>
2472 IDBObjectStore::CreateIndexInternal(const IndexInfo& aInfo, ErrorResult& aRv)
2473 {
2474 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2476 IndexInfo* indexInfo = mInfo->indexes.AppendElement();
2478 indexInfo->name = aInfo.name;
2479 indexInfo->id = aInfo.id;
2480 indexInfo->keyPath = aInfo.keyPath;
2481 indexInfo->unique = aInfo.unique;
2482 indexInfo->multiEntry = aInfo.multiEntry;
2484 // Don't leave this in the list if we fail below!
2485 AutoRemoveIndex autoRemove(mInfo, aInfo.name);
2487 nsRefPtr<IDBIndex> index = IDBIndex::Create(this, indexInfo, true);
2489 mCreatedIndexes.AppendElement(index);
2491 if (IndexedDatabaseManager::IsMainProcess()) {
2492 nsRefPtr<CreateIndexHelper> helper =
2493 new CreateIndexHelper(mTransaction, index);
2495 nsresult rv = helper->DispatchToTransactionPool();
2496 if (NS_FAILED(rv)) {
2497 IDB_WARNING("Failed to dispatch!");
2498 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2499 return nullptr;
2500 }
2501 }
2503 autoRemove.forget();
2505 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
2506 "database(%s).transaction(%s).objectStore(%s)."
2507 "createIndex(%s)",
2508 "MT IDBObjectStore.createIndex()",
2509 IDB_PROFILER_STRING(Transaction()->Database()),
2510 IDB_PROFILER_STRING(Transaction()),
2511 IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index));
2513 return index.forget();
2514 }
2516 already_AddRefed<IDBIndex>
2517 IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
2518 {
2519 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2521 if (mTransaction->IsFinished()) {
2522 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2523 return nullptr;
2524 }
2526 IndexInfo* indexInfo = nullptr;
2527 uint32_t indexCount = mInfo->indexes.Length();
2528 for (uint32_t index = 0; index < indexCount; index++) {
2529 if (mInfo->indexes[index].name == aName) {
2530 indexInfo = &(mInfo->indexes[index]);
2531 break;
2532 }
2533 }
2535 if (!indexInfo) {
2536 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
2537 return nullptr;
2538 }
2540 nsRefPtr<IDBIndex> retval;
2541 for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) {
2542 nsRefPtr<IDBIndex>& index = mCreatedIndexes[i];
2543 if (index->Name() == aName) {
2544 retval = index;
2545 break;
2546 }
2547 }
2549 if (!retval) {
2550 retval = IDBIndex::Create(this, indexInfo, false);
2551 if (!retval) {
2552 IDB_WARNING("Failed to create index!");
2553 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2554 return nullptr;
2555 }
2557 if (!mCreatedIndexes.AppendElement(retval)) {
2558 IDB_WARNING("Out of memory!");
2559 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2560 return nullptr;
2561 }
2562 }
2564 return retval.forget();
2565 }
2567 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
2569 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
2570 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2571 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
2572 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
2575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
2576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
2578 for (uint32_t i = 0; i < tmp->mCreatedIndexes.Length(); i++) {
2579 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]");
2580 cb.NoteXPCOMChild(static_cast<nsISupports*>(tmp->mCreatedIndexes[i].get()));
2581 }
2582 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2584 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
2585 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
2587 // Don't unlink mTransaction!
2589 tmp->mCreatedIndexes.Clear();
2591 tmp->mCachedKeyPath = JSVAL_VOID;
2593 if (tmp->mRooted) {
2594 mozilla::DropJSObjects(tmp);
2595 tmp->mRooted = false;
2596 }
2597 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2599 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
2600 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
2601 NS_INTERFACE_MAP_ENTRY(nsISupports)
2602 NS_INTERFACE_MAP_END
2604 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
2605 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
2607 JSObject*
2608 IDBObjectStore::WrapObject(JSContext* aCx)
2609 {
2610 return IDBObjectStoreBinding::Wrap(aCx, this);
2611 }
2613 void
2614 IDBObjectStore::GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
2615 ErrorResult& aRv)
2616 {
2617 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2619 if (!JSVAL_IS_VOID(mCachedKeyPath)) {
2620 JS::ExposeValueToActiveJS(mCachedKeyPath);
2621 aResult.set(mCachedKeyPath);
2622 return;
2623 }
2625 aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
2626 if (NS_WARN_IF(aRv.Failed())) {
2627 return;
2628 }
2630 if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
2631 mozilla::HoldJSObjects(this);
2632 mRooted = true;
2633 }
2635 JS::ExposeValueToActiveJS(mCachedKeyPath);
2636 aResult.set(mCachedKeyPath);
2637 }
2639 already_AddRefed<DOMStringList>
2640 IDBObjectStore::GetIndexNames(ErrorResult& aRv)
2641 {
2642 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2644 nsRefPtr<DOMStringList> list(new DOMStringList());
2646 nsTArray<nsString>& names = list->StringArray();
2647 uint32_t count = mInfo->indexes.Length();
2648 names.SetCapacity(count);
2650 for (uint32_t index = 0; index < count; index++) {
2651 names.InsertElementSorted(mInfo->indexes[index].name);
2652 }
2654 return list.forget();
2655 }
2657 already_AddRefed<IDBRequest>
2658 IDBObjectStore::Get(JSContext* aCx, JS::Handle<JS::Value> aKey,
2659 ErrorResult& aRv)
2660 {
2661 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2663 if (!mTransaction->IsOpen()) {
2664 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2665 return nullptr;
2666 }
2668 nsRefPtr<IDBKeyRange> keyRange;
2669 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2670 ENSURE_SUCCESS(aRv, nullptr);
2672 if (!keyRange) {
2673 // Must specify a key or keyRange for get().
2674 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
2675 return nullptr;
2676 }
2678 return GetInternal(keyRange, aRv);
2679 }
2681 already_AddRefed<IDBRequest>
2682 IDBObjectStore::GetAll(JSContext* aCx,
2683 JS::Handle<JS::Value> aKey,
2684 const Optional<uint32_t>& aLimit, ErrorResult& aRv)
2685 {
2686 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2688 if (!mTransaction->IsOpen()) {
2689 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2690 return nullptr;
2691 }
2693 nsRefPtr<IDBKeyRange> keyRange;
2694 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2695 ENSURE_SUCCESS(aRv, nullptr);
2697 uint32_t limit = UINT32_MAX;
2698 if (aLimit.WasPassed() && aLimit.Value() != 0) {
2699 limit = aLimit.Value();
2700 }
2702 return GetAllInternal(keyRange, limit, aRv);
2703 }
2705 already_AddRefed<IDBRequest>
2706 IDBObjectStore::Delete(JSContext* aCx, JS::Handle<JS::Value> aKey,
2707 ErrorResult& aRv)
2708 {
2709 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2711 if (!mTransaction->IsOpen()) {
2712 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2713 return nullptr;
2714 }
2716 if (!IsWriteAllowed()) {
2717 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
2718 return nullptr;
2719 }
2721 nsRefPtr<IDBKeyRange> keyRange;
2722 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2723 ENSURE_SUCCESS(aRv, nullptr);
2725 if (!keyRange) {
2726 // Must specify a key or keyRange for delete().
2727 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
2728 return nullptr;
2729 }
2731 return DeleteInternal(keyRange, aRv);
2732 }
2734 already_AddRefed<IDBRequest>
2735 IDBObjectStore::OpenCursor(JSContext* aCx,
2736 JS::Handle<JS::Value> aRange,
2737 IDBCursorDirection aDirection, ErrorResult& aRv)
2738 {
2739 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2741 if (!mTransaction->IsOpen()) {
2742 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2743 return nullptr;
2744 }
2746 nsRefPtr<IDBKeyRange> keyRange;
2747 aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
2748 ENSURE_SUCCESS(aRv, nullptr);
2750 IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
2751 size_t argDirection = static_cast<size_t>(direction);
2753 return OpenCursorInternal(keyRange, argDirection, aRv);
2754 }
2756 already_AddRefed<IDBIndex>
2757 IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName,
2758 const nsAString& aKeyPath,
2759 const IDBIndexParameters& aOptionalParameters,
2760 ErrorResult& aRv)
2761 {
2762 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2764 KeyPath keyPath(0);
2765 if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) ||
2766 !keyPath.IsValid()) {
2767 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2768 return nullptr;
2769 }
2771 return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv);
2772 }
2774 already_AddRefed<IDBIndex>
2775 IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName,
2776 const Sequence<nsString >& aKeyPath,
2777 const IDBIndexParameters& aOptionalParameters,
2778 ErrorResult& aRv)
2779 {
2780 NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
2782 if (!aKeyPath.Length()) {
2783 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2784 return nullptr;
2785 }
2787 KeyPath keyPath(0);
2788 if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath))) {
2789 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
2790 return nullptr;
2791 }
2793 return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv);
2794 }
2796 already_AddRefed<IDBIndex>
2797 IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName,
2798 KeyPath& aKeyPath,
2799 const IDBIndexParameters& aOptionalParameters,
2800 ErrorResult& aRv)
2801 {
2802 // Check name and current mode
2803 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
2805 if (!transaction ||
2806 transaction != mTransaction ||
2807 mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
2808 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2809 return nullptr;
2810 }
2812 bool found = false;
2813 uint32_t indexCount = mInfo->indexes.Length();
2814 for (uint32_t index = 0; index < indexCount; index++) {
2815 if (mInfo->indexes[index].name == aName) {
2816 found = true;
2817 break;
2818 }
2819 }
2821 if (found) {
2822 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
2823 return nullptr;
2824 }
2826 NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
2828 #ifdef DEBUG
2829 for (uint32_t index = 0; index < mCreatedIndexes.Length(); index++) {
2830 if (mCreatedIndexes[index]->Name() == aName) {
2831 NS_ERROR("Already created this one!");
2832 }
2833 }
2834 #endif
2836 if (aOptionalParameters.mMultiEntry && aKeyPath.IsArray()) {
2837 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2838 return nullptr;
2839 }
2841 DatabaseInfo* databaseInfo = mTransaction->DBInfo();
2843 IndexInfo info;
2845 info.name = aName;
2846 info.id = databaseInfo->nextIndexId++;
2847 info.keyPath = aKeyPath;
2848 info.unique = aOptionalParameters.mUnique;
2849 info.multiEntry = aOptionalParameters.mMultiEntry;
2851 return CreateIndexInternal(info, aRv);
2852 }
2854 void
2855 IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
2856 {
2857 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2859 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
2861 if (!transaction ||
2862 transaction != mTransaction ||
2863 mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
2864 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
2865 return;
2866 }
2868 NS_ASSERTION(mTransaction->IsOpen(), "Impossible!");
2870 uint32_t index = 0;
2871 for (; index < mInfo->indexes.Length(); index++) {
2872 if (mInfo->indexes[index].name == aName) {
2873 break;
2874 }
2875 }
2877 if (index == mInfo->indexes.Length()) {
2878 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
2879 return;
2880 }
2882 if (IndexedDatabaseManager::IsMainProcess()) {
2883 nsRefPtr<DeleteIndexHelper> helper =
2884 new DeleteIndexHelper(mTransaction, this, aName);
2886 nsresult rv = helper->DispatchToTransactionPool();
2887 if (NS_FAILED(rv)) {
2888 IDB_WARNING("Failed to dispatch!");
2889 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2890 return;
2891 }
2892 }
2893 else {
2894 NS_ASSERTION(mActorChild, "Must have an actor here!");
2896 mActorChild->SendDeleteIndex(nsString(aName));
2897 }
2899 mInfo->indexes.RemoveElementAt(index);
2901 for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) {
2902 if (mCreatedIndexes[i]->Name() == aName) {
2903 mCreatedIndexes.RemoveElementAt(i);
2904 break;
2905 }
2906 }
2908 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
2909 "database(%s).transaction(%s).objectStore(%s)."
2910 "deleteIndex(\"%s\")",
2911 "MT IDBObjectStore.deleteIndex()",
2912 IDB_PROFILER_STRING(Transaction()->Database()),
2913 IDB_PROFILER_STRING(Transaction()),
2914 IDB_PROFILER_STRING(this),
2915 NS_ConvertUTF16toUTF8(aName).get());
2916 }
2918 already_AddRefed<IDBRequest>
2919 IDBObjectStore::Count(JSContext* aCx,
2920 JS::Handle<JS::Value> aKey,
2921 ErrorResult& aRv)
2922 {
2923 if (!mTransaction->IsOpen()) {
2924 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2925 return nullptr;
2926 }
2928 nsRefPtr<IDBKeyRange> keyRange;
2929 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2930 ENSURE_SUCCESS(aRv, nullptr);
2932 return CountInternal(keyRange, aRv);
2933 }
2935 already_AddRefed<IDBRequest>
2936 IDBObjectStore::GetAllKeys(JSContext* aCx,
2937 JS::Handle<JS::Value> aKey,
2938 const Optional<uint32_t>& aLimit, ErrorResult& aRv)
2939 {
2940 MOZ_ASSERT(NS_IsMainThread());
2942 if (!mTransaction->IsOpen()) {
2943 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2944 return nullptr;
2945 }
2947 nsRefPtr<IDBKeyRange> keyRange;
2948 aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
2949 ENSURE_SUCCESS(aRv, nullptr);
2951 uint32_t limit = UINT32_MAX;
2952 if (aLimit.WasPassed() && aLimit.Value() != 0) {
2953 limit = aLimit.Value();
2954 }
2956 return GetAllKeysInternal(keyRange, limit, aRv);
2957 }
2959 already_AddRefed<IDBRequest>
2960 IDBObjectStore::OpenKeyCursor(JSContext* aCx,
2961 JS::Handle<JS::Value> aRange,
2962 IDBCursorDirection aDirection, ErrorResult& aRv)
2963 {
2964 MOZ_ASSERT(NS_IsMainThread());
2966 if (!mTransaction->IsOpen()) {
2967 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
2968 return nullptr;
2969 }
2971 nsRefPtr<IDBKeyRange> keyRange;
2972 aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
2973 ENSURE_SUCCESS(aRv, nullptr);
2975 IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
2977 return OpenKeyCursorInternal(keyRange, static_cast<size_t>(direction), aRv);
2978 }
2980 inline nsresult
2981 CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
2982 {
2983 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
2984 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
2986 PROFILER_LABEL("IndexedDB", "CopyData");
2988 nsresult rv;
2990 do {
2991 char copyBuffer[FILE_COPY_BUFFER_SIZE];
2993 uint32_t numRead;
2994 rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
2995 NS_ENSURE_SUCCESS(rv, rv);
2997 if (!numRead) {
2998 break;
2999 }
3001 uint32_t numWrite;
3002 rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
3003 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
3004 rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
3005 }
3006 NS_ENSURE_SUCCESS(rv, rv);
3008 NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
3009 } while (true);
3011 rv = aOutputStream->Flush();
3012 NS_ENSURE_SUCCESS(rv, rv);
3014 return NS_OK;
3015 }
3017 void
3018 ObjectStoreHelper::ReleaseMainThreadObjects()
3019 {
3020 mObjectStore = nullptr;
3021 AsyncConnectionHelper::ReleaseMainThreadObjects();
3022 }
3024 nsresult
3025 ObjectStoreHelper::Dispatch(nsIEventTarget* aDatabaseThread)
3026 {
3027 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3029 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "ObjectStoreHelper::Dispatch");
3031 if (IndexedDatabaseManager::IsMainProcess()) {
3032 return AsyncConnectionHelper::Dispatch(aDatabaseThread);
3033 }
3035 // If we've been invalidated then there's no point sending anything to the
3036 // parent process.
3037 if (mObjectStore->Transaction()->Database()->IsInvalidated()) {
3038 IDB_REPORT_INTERNAL_ERR();
3039 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3040 }
3042 IndexedDBObjectStoreChild* objectStoreActor = mObjectStore->GetActorChild();
3043 NS_ASSERTION(objectStoreActor, "Must have an actor here!");
3045 ObjectStoreRequestParams params;
3046 nsresult rv = PackArgumentsForParentProcess(params);
3047 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3049 NoDispatchEventTarget target;
3050 rv = AsyncConnectionHelper::Dispatch(&target);
3051 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3053 mActor =
3054 new IndexedDBObjectStoreRequestChild(this, mObjectStore, params.type());
3055 objectStoreActor->SendPIndexedDBRequestConstructor(mActor, params);
3057 return NS_OK;
3058 }
3060 void
3061 NoRequestObjectStoreHelper::ReleaseMainThreadObjects()
3062 {
3063 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3064 mObjectStore = nullptr;
3065 AsyncConnectionHelper::ReleaseMainThreadObjects();
3066 }
3068 nsresult
3069 NoRequestObjectStoreHelper::UnpackResponseFromParentProcess(
3070 const ResponseValue& aResponseValue)
3071 {
3072 MOZ_CRASH();
3073 }
3075 AsyncConnectionHelper::ChildProcessSendResult
3076 NoRequestObjectStoreHelper::SendResponseToChildProcess(nsresult aResultCode)
3077 {
3078 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3079 return Success_NotSent;
3080 }
3082 nsresult
3083 NoRequestObjectStoreHelper::OnSuccess()
3084 {
3085 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3086 return NS_OK;
3087 }
3089 void
3090 NoRequestObjectStoreHelper::OnError()
3091 {
3092 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3093 mTransaction->Abort(GetResultCode());
3094 }
3096 // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
3097 uint64_t
3098 ReinterpretDoubleAsUInt64(double d)
3099 {
3100 union {
3101 double d;
3102 uint64_t u;
3103 } pun;
3104 pun.d = d;
3105 return pun.u;
3106 }
3108 nsresult
3109 AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
3110 {
3111 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3112 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3113 NS_ASSERTION(aConnection, "Passed a null connection!");
3115 PROFILER_LABEL("IndexedDB", "AddHelper::DoDatabaseWork");
3117 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
3118 NS_WARNING("Refusing to add more data because disk space is low!");
3119 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
3120 }
3122 nsresult rv;
3123 bool keyUnset = mKey.IsUnset();
3124 int64_t osid = mObjectStore->Id();
3125 const KeyPath& keyPath = mObjectStore->GetKeyPath();
3127 // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
3128 // specified we should never have a collision and so it shouldn't matter
3129 // if we allow overwrite or not. By not allowing overwrite we raise
3130 // detectable errors rather than corrupting data
3131 nsCOMPtr<mozIStorageStatement> stmt = !mOverwrite || keyUnset ?
3132 mTransaction->GetCachedStatement(
3133 "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
3134 "VALUES (:osid, :key_value, :data, :file_ids)") :
3135 mTransaction->GetCachedStatement(
3136 "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, "
3137 "file_ids) "
3138 "VALUES (:osid, :key_value, :data, :file_ids)");
3139 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3141 mozStorageStatementScoper scoper(stmt);
3143 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
3144 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3146 NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(),
3147 "Should have key unless autoincrement");
3149 int64_t autoIncrementNum = 0;
3151 if (mObjectStore->IsAutoIncrement()) {
3152 if (keyUnset) {
3153 autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId;
3155 MOZ_ASSERT(autoIncrementNum > 0,
3156 "Generated key must always be a positive integer");
3158 if (autoIncrementNum > (1LL << 53)) {
3159 IDB_REPORT_INTERNAL_ERR();
3160 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3161 }
3163 mKey.SetFromInteger(autoIncrementNum);
3164 }
3165 else if (mKey.IsFloat() &&
3166 mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) {
3167 autoIncrementNum = floor(mKey.ToFloat());
3168 }
3170 if (keyUnset && keyPath.IsValid()) {
3171 // Special case where someone put an object into an autoIncrement'ing
3172 // objectStore with no key in its keyPath set. We needed to figure out
3173 // which row id we would get above before we could set that properly.
3175 LittleEndian::writeUint64((char*)mCloneWriteInfo.mCloneBuffer.data() +
3176 mCloneWriteInfo.mOffsetToKeyProp,
3177 ReinterpretDoubleAsUInt64(static_cast<double>(
3178 autoIncrementNum)));
3179 }
3180 }
3182 mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value"));
3185 // Compress the bytes before adding into the database.
3186 const char* uncompressed =
3187 reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
3188 size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
3190 // We don't have a smart pointer class that calls moz_free, so we need to
3191 // manage | compressed | manually.
3192 {
3193 size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
3194 // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage
3195 // expects to be able to free the adopted pointer with NS_Free.
3196 char* compressed = (char*)moz_malloc(compressedLength);
3197 NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY);
3199 snappy::RawCompress(uncompressed, uncompressedLength, compressed,
3200 &compressedLength);
3202 uint8_t* dataBuffer = reinterpret_cast<uint8_t*>(compressed);
3203 size_t dataBufferLength = compressedLength;
3205 // If this call succeeds, | compressed | is now owned by the statement, and
3206 // we are no longer responsible for it.
3207 rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer,
3208 dataBufferLength);
3209 if (NS_FAILED(rv)) {
3210 moz_free(compressed);
3211 }
3212 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3213 }
3215 // Handle blobs
3216 uint32_t length = mCloneWriteInfo.mFiles.Length();
3217 if (length) {
3218 nsRefPtr<FileManager> fileManager = mDatabase->Manager();
3220 nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
3221 IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3223 nsCOMPtr<nsIFile> journalDirectory = fileManager->EnsureJournalDirectory();
3224 IDB_ENSURE_TRUE(journalDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3226 nsAutoString fileIds;
3228 for (uint32_t index = 0; index < length; index++) {
3229 StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index];
3231 FileInfo* fileInfo = cloneFile.mFileInfo;
3232 nsIInputStream* inputStream = cloneFile.mInputStream;
3234 int64_t id = fileInfo->Id();
3235 if (inputStream) {
3236 // Create a journal file first
3237 nsCOMPtr<nsIFile> nativeFile =
3238 fileManager->GetFileForId(journalDirectory, id);
3239 IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3241 rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
3242 IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3244 // Now we can copy the blob
3245 nativeFile = fileManager->GetFileForId(directory, id);
3246 IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3248 IDBDatabase* database = mObjectStore->Transaction()->Database();
3249 nsRefPtr<FileOutputStream> outputStream =
3250 FileOutputStream::Create(database->Type(), database->Group(),
3251 database->Origin(), nativeFile);
3252 IDB_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3254 rv = CopyData(inputStream, outputStream);
3255 if (NS_FAILED(rv) &&
3256 NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
3257 IDB_REPORT_INTERNAL_ERR();
3258 rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3259 }
3260 NS_ENSURE_SUCCESS(rv, rv);
3262 cloneFile.mFile->AddFileInfo(fileInfo);
3263 }
3265 if (index) {
3266 fileIds.Append(NS_LITERAL_STRING(" "));
3267 }
3268 fileIds.AppendInt(id);
3269 }
3271 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
3272 }
3273 else {
3274 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
3275 }
3276 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3278 rv = stmt->Execute();
3279 if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
3280 NS_ASSERTION(!keyUnset, "Generated key had a collision!?");
3281 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
3282 }
3283 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3285 int64_t objectDataId;
3286 rv = aConnection->GetLastInsertRowID(&objectDataId);
3287 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3289 // Update our indexes if needed.
3290 if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) {
3291 rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite,
3292 objectDataId, mIndexUpdateInfo);
3293 if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
3294 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
3295 }
3296 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3297 }
3299 if (autoIncrementNum) {
3300 mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1;
3301 }
3303 return NS_OK;
3304 }
3306 nsresult
3307 AddHelper::GetSuccessResult(JSContext* aCx,
3308 JS::MutableHandle<JS::Value> aVal)
3309 {
3310 NS_ASSERTION(!mKey.IsUnset(), "Badness!");
3312 mCloneWriteInfo.mCloneBuffer.clear();
3314 return mKey.ToJSVal(aCx, aVal);
3315 }
3317 void
3318 AddHelper::ReleaseMainThreadObjects()
3319 {
3320 IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
3321 ObjectStoreHelper::ReleaseMainThreadObjects();
3322 }
3324 nsresult
3325 AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
3326 {
3327 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3328 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3330 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3331 "AddHelper::PackArgumentsForParentProcess");
3333 AddPutParams commonParams;
3334 commonParams.cloneInfo() = mCloneWriteInfo;
3335 commonParams.key() = mKey;
3336 commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo);
3338 const nsTArray<StructuredCloneFile>& files = mCloneWriteInfo.mFiles;
3340 if (!files.IsEmpty()) {
3341 uint32_t fileCount = files.Length();
3343 InfallibleTArray<PBlobChild*>& blobsChild = commonParams.blobsChild();
3344 blobsChild.SetCapacity(fileCount);
3346 ContentChild* contentChild = ContentChild::GetSingleton();
3347 NS_ASSERTION(contentChild, "This should never be null!");
3349 for (uint32_t index = 0; index < fileCount; index++) {
3350 const StructuredCloneFile& file = files[index];
3352 NS_ASSERTION(file.mFile, "This should never be null!");
3353 NS_ASSERTION(!file.mFileInfo, "This is not yet supported!");
3355 BlobChild* actor =
3356 contentChild->GetOrCreateActorForBlob(file.mFile);
3357 if (!actor) {
3358 IDB_REPORT_INTERNAL_ERR();
3359 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3360 }
3361 blobsChild.AppendElement(actor);
3362 }
3363 }
3365 if (mOverwrite) {
3366 PutParams putParams;
3367 putParams.commonParams() = commonParams;
3368 aParams = putParams;
3369 }
3370 else {
3371 AddParams addParams;
3372 addParams.commonParams() = commonParams;
3373 aParams = addParams;
3374 }
3376 return NS_OK;
3377 }
3379 AsyncConnectionHelper::ChildProcessSendResult
3380 AddHelper::SendResponseToChildProcess(nsresult aResultCode)
3381 {
3382 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3383 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3385 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3386 "AddHelper::SendResponseToChildProcess");
3388 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3389 NS_ASSERTION(actor, "How did we get this far without an actor?");
3391 ResponseValue response;
3392 if (NS_FAILED(aResultCode)) {
3393 response = aResultCode;
3394 }
3395 else if (mOverwrite) {
3396 PutResponse putResponse;
3397 putResponse.key() = mKey;
3398 response = putResponse;
3399 }
3400 else {
3401 AddResponse addResponse;
3402 addResponse.key() = mKey;
3403 response = addResponse;
3404 }
3406 if (!actor->SendResponse(response)) {
3407 return Error;
3408 }
3410 return Success_Sent;
3411 }
3413 nsresult
3414 AddHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
3415 {
3416 NS_ASSERTION(aResponseValue.type() == ResponseValue::TAddResponse ||
3417 aResponseValue.type() == ResponseValue::TPutResponse,
3418 "Bad response type!");
3420 mKey = mOverwrite ?
3421 aResponseValue.get_PutResponse().key() :
3422 aResponseValue.get_AddResponse().key();
3424 return NS_OK;
3425 }
3427 nsresult
3428 GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
3429 {
3430 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3431 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3432 NS_ASSERTION(mKeyRange, "Must have a key range here!");
3434 PROFILER_LABEL("IndexedDB", "GetHelper::DoDatabaseWork [IDBObjectStore.cpp]");
3436 nsCString keyRangeClause;
3437 mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause);
3439 NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
3441 nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
3442 "WHERE object_store_id = :osid") +
3443 keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
3445 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
3446 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3448 mozStorageStatementScoper scoper(stmt);
3450 nsresult rv =
3451 stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->Id());
3452 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3454 rv = mKeyRange->BindToStatement(stmt);
3455 NS_ENSURE_SUCCESS(rv, rv);
3457 bool hasResult;
3458 rv = stmt->ExecuteStep(&hasResult);
3459 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3461 if (hasResult) {
3462 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
3463 mDatabase, mCloneReadInfo);
3464 NS_ENSURE_SUCCESS(rv, rv);
3465 }
3467 return NS_OK;
3468 }
3470 nsresult
3471 GetHelper::GetSuccessResult(JSContext* aCx,
3472 JS::MutableHandle<JS::Value> aVal)
3473 {
3474 bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal);
3476 mCloneReadInfo.mCloneBuffer.clear();
3478 NS_ENSURE_TRUE(result, NS_ERROR_DOM_DATA_CLONE_ERR);
3479 return NS_OK;
3480 }
3482 void
3483 GetHelper::ReleaseMainThreadObjects()
3484 {
3485 mKeyRange = nullptr;
3486 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
3487 ObjectStoreHelper::ReleaseMainThreadObjects();
3488 }
3490 nsresult
3491 GetHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
3492 {
3493 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3494 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3495 NS_ASSERTION(mKeyRange, "This should never be null!");
3497 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3498 "GetHelper::PackArgumentsForParentProcess "
3499 "[IDBObjectStore.cpp]");
3501 GetParams params;
3503 mKeyRange->ToSerializedKeyRange(params.keyRange());
3505 aParams = params;
3506 return NS_OK;
3507 }
3509 AsyncConnectionHelper::ChildProcessSendResult
3510 GetHelper::SendResponseToChildProcess(nsresult aResultCode)
3511 {
3512 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3513 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3515 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3516 "GetHelper::SendResponseToChildProcess "
3517 "[IDBObjectStore.cpp]");
3519 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3520 NS_ASSERTION(actor, "How did we get this far without an actor?");
3522 InfallibleTArray<PBlobParent*> blobsParent;
3524 if (NS_SUCCEEDED(aResultCode)) {
3525 IDBDatabase* database = mObjectStore->Transaction()->Database();
3526 NS_ASSERTION(database, "This should never be null!");
3528 ContentParent* contentParent = database->GetContentParent();
3529 NS_ASSERTION(contentParent, "This should never be null!");
3531 FileManager* fileManager = database->Manager();
3532 NS_ASSERTION(fileManager, "This should never be null!");
3534 const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
3536 aResultCode =
3537 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
3538 blobsParent);
3539 if (NS_FAILED(aResultCode)) {
3540 NS_WARNING("ConvertBlobsToActors failed!");
3541 }
3542 }
3544 ResponseValue response;
3545 if (NS_FAILED(aResultCode)) {
3546 response = aResultCode;
3547 }
3548 else {
3549 GetResponse getResponse;
3550 getResponse.cloneInfo() = mCloneReadInfo;
3551 getResponse.blobsParent().SwapElements(blobsParent);
3552 response = getResponse;
3553 }
3555 if (!actor->SendResponse(response)) {
3556 return Error;
3557 }
3559 return Success_Sent;
3560 }
3562 nsresult
3563 GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
3564 {
3565 NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse,
3566 "Bad response type!");
3568 const GetResponse& getResponse = aResponseValue.get_GetResponse();
3569 const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo();
3571 NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
3572 (cloneInfo.dataLength && cloneInfo.data),
3573 "Inconsistent clone info!");
3575 if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
3576 IDB_WARNING("Failed to copy clone buffer!");
3577 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
3578 }
3580 IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(),
3581 mCloneReadInfo.mFiles);
3582 return NS_OK;
3583 }
3585 nsresult
3586 DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */)
3587 {
3588 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3589 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3590 NS_ASSERTION(mKeyRange, "Must have a key range here!");
3592 PROFILER_LABEL("IndexedDB", "DeleteHelper::DoDatabaseWork");
3594 nsCString keyRangeClause;
3595 mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause);
3597 NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!");
3599 nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data "
3600 "WHERE object_store_id = :osid") +
3601 keyRangeClause;
3603 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
3604 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3606 mozStorageStatementScoper scoper(stmt);
3608 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
3609 mObjectStore->Id());
3610 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3612 rv = mKeyRange->BindToStatement(stmt);
3613 NS_ENSURE_SUCCESS(rv, rv);
3615 rv = stmt->Execute();
3616 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3618 return NS_OK;
3619 }
3621 nsresult
3622 DeleteHelper::GetSuccessResult(JSContext* aCx,
3623 JS::MutableHandle<JS::Value> aVal)
3624 {
3625 aVal.setUndefined();
3626 return NS_OK;
3627 }
3629 nsresult
3630 DeleteHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
3631 {
3632 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3633 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3634 NS_ASSERTION(mKeyRange, "This should never be null!");
3636 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3637 "DeleteHelper::PackArgumentsForParentProcess");
3639 DeleteParams params;
3641 mKeyRange->ToSerializedKeyRange(params.keyRange());
3643 aParams = params;
3644 return NS_OK;
3645 }
3647 AsyncConnectionHelper::ChildProcessSendResult
3648 DeleteHelper::SendResponseToChildProcess(nsresult aResultCode)
3649 {
3650 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3651 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3653 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3654 "DeleteHelper::SendResponseToChildProcess");
3656 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3657 NS_ASSERTION(actor, "How did we get this far without an actor?");
3659 ResponseValue response;
3660 if (NS_FAILED(aResultCode)) {
3661 response = aResultCode;
3662 }
3663 else {
3664 response = DeleteResponse();
3665 }
3667 if (!actor->SendResponse(response)) {
3668 return Error;
3669 }
3671 return Success_Sent;
3672 }
3674 nsresult
3675 DeleteHelper::UnpackResponseFromParentProcess(
3676 const ResponseValue& aResponseValue)
3677 {
3678 NS_ASSERTION(aResponseValue.type() == ResponseValue::TDeleteResponse,
3679 "Bad response type!");
3681 return NS_OK;
3682 }
3684 nsresult
3685 ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
3686 {
3687 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3688 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3689 NS_ASSERTION(aConnection, "Passed a null connection!");
3691 PROFILER_LABEL("IndexedDB", "ClearHelper::DoDatabaseWork");
3693 nsCOMPtr<mozIStorageStatement> stmt =
3694 mTransaction->GetCachedStatement(
3695 NS_LITERAL_CSTRING("DELETE FROM object_data "
3696 "WHERE object_store_id = :osid"));
3697 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3699 mozStorageStatementScoper scoper(stmt);
3701 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
3702 mObjectStore->Id());
3703 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3705 rv = stmt->Execute();
3706 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3708 return NS_OK;
3709 }
3711 nsresult
3712 ClearHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
3713 {
3714 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3715 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3717 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3718 "ClearHelper::PackArgumentsForParentProcess");
3720 aParams = ClearParams();
3721 return NS_OK;
3722 }
3724 AsyncConnectionHelper::ChildProcessSendResult
3725 ClearHelper::SendResponseToChildProcess(nsresult aResultCode)
3726 {
3727 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3728 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3730 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3731 "ClearHelper::SendResponseToChildProcess");
3733 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3734 NS_ASSERTION(actor, "How did we get this far without an actor?");
3736 ResponseValue response;
3737 if (NS_FAILED(aResultCode)) {
3738 response = aResultCode;
3739 }
3740 else {
3741 response = ClearResponse();
3742 }
3744 if (!actor->SendResponse(response)) {
3745 return Error;
3746 }
3748 return Success_Sent;
3749 }
3751 nsresult
3752 ClearHelper::UnpackResponseFromParentProcess(
3753 const ResponseValue& aResponseValue)
3754 {
3755 NS_ASSERTION(aResponseValue.type() == ResponseValue::TClearResponse,
3756 "Bad response type!");
3758 return NS_OK;
3759 }
3761 nsresult
3762 OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
3763 {
3764 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3765 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3767 PROFILER_LABEL("IndexedDB",
3768 "OpenCursorHelper::DoDatabaseWork [IDBObjectStore.cpp]");
3770 NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
3772 nsCString keyRangeClause;
3773 if (mKeyRange) {
3774 mKeyRange->GetBindingClause(keyValue, keyRangeClause);
3775 }
3777 nsAutoCString directionClause;
3778 switch (mDirection) {
3779 case IDBCursor::NEXT:
3780 case IDBCursor::NEXT_UNIQUE:
3781 directionClause.AssignLiteral(" ORDER BY key_value ASC");
3782 break;
3784 case IDBCursor::PREV:
3785 case IDBCursor::PREV_UNIQUE:
3786 directionClause.AssignLiteral(" ORDER BY key_value DESC");
3787 break;
3789 default:
3790 NS_NOTREACHED("Unknown direction type!");
3791 }
3793 nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids "
3794 "FROM object_data "
3795 "WHERE object_store_id = :id") +
3796 keyRangeClause + directionClause +
3797 NS_LITERAL_CSTRING(" LIMIT 1");
3799 nsCOMPtr<mozIStorageStatement> stmt =
3800 mTransaction->GetCachedStatement(firstQuery);
3801 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3803 mozStorageStatementScoper scoper(stmt);
3805 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
3806 mObjectStore->Id());
3807 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3809 if (mKeyRange) {
3810 rv = mKeyRange->BindToStatement(stmt);
3811 NS_ENSURE_SUCCESS(rv, rv);
3812 }
3814 bool hasResult;
3815 rv = stmt->ExecuteStep(&hasResult);
3816 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3818 if (!hasResult) {
3819 mKey.Unset();
3820 return NS_OK;
3821 }
3823 rv = mKey.SetFromStatement(stmt, 0);
3824 NS_ENSURE_SUCCESS(rv, rv);
3826 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
3827 mDatabase, mCloneReadInfo);
3828 NS_ENSURE_SUCCESS(rv, rv);
3830 // Now we need to make the query to get the next match.
3831 keyRangeClause.Truncate();
3832 nsAutoCString continueToKeyRangeClause;
3834 NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
3835 NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
3837 switch (mDirection) {
3838 case IDBCursor::NEXT:
3839 case IDBCursor::NEXT_UNIQUE:
3840 AppendConditionClause(keyValue, currentKey, false, false,
3841 keyRangeClause);
3842 AppendConditionClause(keyValue, currentKey, false, true,
3843 continueToKeyRangeClause);
3844 if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
3845 AppendConditionClause(keyValue, rangeKey, true,
3846 !mKeyRange->IsUpperOpen(), keyRangeClause);
3847 AppendConditionClause(keyValue, rangeKey, true,
3848 !mKeyRange->IsUpperOpen(),
3849 continueToKeyRangeClause);
3850 mRangeKey = mKeyRange->Upper();
3851 }
3852 break;
3854 case IDBCursor::PREV:
3855 case IDBCursor::PREV_UNIQUE:
3856 AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
3857 AppendConditionClause(keyValue, currentKey, true, true,
3858 continueToKeyRangeClause);
3859 if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
3860 AppendConditionClause(keyValue, rangeKey, false,
3861 !mKeyRange->IsLowerOpen(), keyRangeClause);
3862 AppendConditionClause(keyValue, rangeKey, false,
3863 !mKeyRange->IsLowerOpen(),
3864 continueToKeyRangeClause);
3865 mRangeKey = mKeyRange->Lower();
3866 }
3867 break;
3869 default:
3870 NS_NOTREACHED("Unknown direction type!");
3871 }
3873 NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids "
3874 "FROM object_data "
3875 "WHERE object_store_id = :id");
3877 mContinueQuery = queryStart + keyRangeClause + directionClause +
3878 NS_LITERAL_CSTRING(" LIMIT ");
3880 mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
3881 NS_LITERAL_CSTRING(" LIMIT ");
3883 return NS_OK;
3884 }
3886 nsresult
3887 OpenCursorHelper::EnsureCursor()
3888 {
3889 if (mCursor || mKey.IsUnset()) {
3890 return NS_OK;
3891 }
3893 mSerializedCloneReadInfo = mCloneReadInfo;
3895 NS_ASSERTION(mSerializedCloneReadInfo.data &&
3896 mSerializedCloneReadInfo.dataLength,
3897 "Shouldn't be possible!");
3899 nsRefPtr<IDBCursor> cursor =
3900 IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
3901 mRangeKey, mContinueQuery, mContinueToQuery, mKey,
3902 Move(mCloneReadInfo));
3903 IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3905 NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
3907 mCursor.swap(cursor);
3908 return NS_OK;
3909 }
3911 nsresult
3912 OpenCursorHelper::GetSuccessResult(JSContext* aCx,
3913 JS::MutableHandle<JS::Value> aVal)
3914 {
3915 nsresult rv = EnsureCursor();
3916 NS_ENSURE_SUCCESS(rv, rv);
3918 if (mCursor) {
3919 rv = WrapNative(aCx, mCursor, aVal);
3920 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
3921 }
3922 else {
3923 aVal.setUndefined();
3924 }
3926 return NS_OK;
3927 }
3929 void
3930 OpenCursorHelper::ReleaseMainThreadObjects()
3931 {
3932 mKeyRange = nullptr;
3933 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
3935 mCursor = nullptr;
3937 // These don't need to be released on the main thread but they're only valid
3938 // as long as mCursor is set.
3939 mSerializedCloneReadInfo.data = nullptr;
3940 mSerializedCloneReadInfo.dataLength = 0;
3942 ObjectStoreHelper::ReleaseMainThreadObjects();
3943 }
3945 nsresult
3946 OpenCursorHelper::PackArgumentsForParentProcess(
3947 ObjectStoreRequestParams& aParams)
3948 {
3949 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3950 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3952 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3953 "OpenCursorHelper::PackArgumentsForParentProcess "
3954 "[IDBObjectStore.cpp]");
3956 OpenCursorParams params;
3958 if (mKeyRange) {
3959 KeyRange keyRange;
3960 mKeyRange->ToSerializedKeyRange(keyRange);
3961 params.optionalKeyRange() = keyRange;
3962 }
3963 else {
3964 params.optionalKeyRange() = mozilla::void_t();
3965 }
3967 params.direction() = mDirection;
3969 aParams = params;
3970 return NS_OK;
3971 }
3973 AsyncConnectionHelper::ChildProcessSendResult
3974 OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
3975 {
3976 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
3977 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
3978 NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
3980 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
3981 "OpenCursorHelper::SendResponseToChildProcess "
3982 "[IDBObjectStore.cpp]");
3984 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
3985 NS_ASSERTION(actor, "How did we get this far without an actor?");
3987 InfallibleTArray<PBlobParent*> blobsParent;
3989 if (NS_SUCCEEDED(aResultCode)) {
3990 IDBDatabase* database = mObjectStore->Transaction()->Database();
3991 NS_ASSERTION(database, "This should never be null!");
3993 ContentParent* contentParent = database->GetContentParent();
3994 NS_ASSERTION(contentParent, "This should never be null!");
3996 FileManager* fileManager = database->Manager();
3997 NS_ASSERTION(fileManager, "This should never be null!");
3999 const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
4001 aResultCode =
4002 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
4003 blobsParent);
4004 if (NS_FAILED(aResultCode)) {
4005 NS_WARNING("ConvertBlobsToActors failed!");
4006 }
4007 }
4009 if (NS_SUCCEEDED(aResultCode)) {
4010 nsresult rv = EnsureCursor();
4011 if (NS_FAILED(rv)) {
4012 NS_WARNING("EnsureCursor failed!");
4013 aResultCode = rv;
4014 }
4015 }
4017 ResponseValue response;
4018 if (NS_FAILED(aResultCode)) {
4019 response = aResultCode;
4020 }
4021 else {
4022 OpenCursorResponse openCursorResponse;
4024 if (!mCursor) {
4025 openCursorResponse = mozilla::void_t();
4026 }
4027 else {
4028 IndexedDBObjectStoreParent* objectStoreActor =
4029 mObjectStore->GetActorParent();
4030 NS_ASSERTION(objectStoreActor, "Must have an actor here!");
4032 IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent();
4033 NS_ASSERTION(requestActor, "Must have an actor here!");
4035 NS_ASSERTION(mSerializedCloneReadInfo.data &&
4036 mSerializedCloneReadInfo.dataLength,
4037 "Shouldn't be possible!");
4039 ObjectStoreCursorConstructorParams params;
4040 params.requestParent() = requestActor;
4041 params.direction() = mDirection;
4042 params.key() = mKey;
4043 params.optionalCloneInfo() = mSerializedCloneReadInfo;
4044 params.blobsParent().SwapElements(blobsParent);
4046 if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
4047 return Error;
4048 }
4049 }
4051 response = openCursorResponse;
4052 }
4054 if (!actor->SendResponse(response)) {
4055 return Error;
4056 }
4058 return Success_Sent;
4059 }
4061 nsresult
4062 OpenCursorHelper::UnpackResponseFromParentProcess(
4063 const ResponseValue& aResponseValue)
4064 {
4065 NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse,
4066 "Bad response type!");
4067 NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() ==
4068 OpenCursorResponse::Tvoid_t ||
4069 aResponseValue.get_OpenCursorResponse().type() ==
4070 OpenCursorResponse::TPIndexedDBCursorChild,
4071 "Bad response union type!");
4072 NS_ASSERTION(!mCursor, "Shouldn't have this yet!");
4074 const OpenCursorResponse& response =
4075 aResponseValue.get_OpenCursorResponse();
4077 switch (response.type()) {
4078 case OpenCursorResponse::Tvoid_t:
4079 break;
4081 case OpenCursorResponse::TPIndexedDBCursorChild: {
4082 IndexedDBCursorChild* actor =
4083 static_cast<IndexedDBCursorChild*>(
4084 response.get_PIndexedDBCursorChild());
4086 mCursor = actor->ForgetStrongCursor();
4087 NS_ASSERTION(mCursor, "This should never be null!");
4089 } break;
4091 default:
4092 MOZ_CRASH();
4093 }
4095 return NS_OK;
4096 }
4098 nsresult
4099 OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
4100 {
4101 MOZ_ASSERT(!NS_IsMainThread());
4102 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4104 PROFILER_LABEL("IndexedDB",
4105 "OpenKeyCursorHelper::DoDatabaseWork [IDBObjectStore.cpp]");
4107 NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
4108 NS_NAMED_LITERAL_CSTRING(id, "id");
4109 NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT ");
4111 nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT ") + keyValue +
4112 NS_LITERAL_CSTRING(" FROM object_data WHERE "
4113 "object_store_id = :") +
4114 id;
4116 nsAutoCString keyRangeClause;
4117 if (mKeyRange) {
4118 mKeyRange->GetBindingClause(keyValue, keyRangeClause);
4119 }
4121 nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue;
4122 switch (mDirection) {
4123 case IDBCursor::NEXT:
4124 case IDBCursor::NEXT_UNIQUE:
4125 directionClause.AppendLiteral(" ASC");
4126 break;
4128 case IDBCursor::PREV:
4129 case IDBCursor::PREV_UNIQUE:
4130 directionClause.AppendLiteral(" DESC");
4131 break;
4133 default:
4134 MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
4135 }
4137 nsCString firstQuery = queryStart + keyRangeClause + directionClause +
4138 openLimit + NS_LITERAL_CSTRING("1");
4140 nsCOMPtr<mozIStorageStatement> stmt =
4141 mTransaction->GetCachedStatement(firstQuery);
4142 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4144 mozStorageStatementScoper scoper(stmt);
4146 nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
4147 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4149 if (mKeyRange) {
4150 rv = mKeyRange->BindToStatement(stmt);
4151 NS_ENSURE_SUCCESS(rv, rv);
4152 }
4154 bool hasResult;
4155 rv = stmt->ExecuteStep(&hasResult);
4156 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4158 if (!hasResult) {
4159 mKey.Unset();
4160 return NS_OK;
4161 }
4163 rv = mKey.SetFromStatement(stmt, 0);
4164 NS_ENSURE_SUCCESS(rv, rv);
4166 // Now we need to make the query to get the next match.
4167 keyRangeClause.Truncate();
4168 nsAutoCString continueToKeyRangeClause;
4170 NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
4171 NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
4173 switch (mDirection) {
4174 case IDBCursor::NEXT:
4175 case IDBCursor::NEXT_UNIQUE:
4176 AppendConditionClause(keyValue, currentKey, false, false,
4177 keyRangeClause);
4178 AppendConditionClause(keyValue, currentKey, false, true,
4179 continueToKeyRangeClause);
4180 if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
4181 AppendConditionClause(keyValue, rangeKey, true,
4182 !mKeyRange->IsUpperOpen(), keyRangeClause);
4183 AppendConditionClause(keyValue, rangeKey, true,
4184 !mKeyRange->IsUpperOpen(),
4185 continueToKeyRangeClause);
4186 mRangeKey = mKeyRange->Upper();
4187 }
4188 break;
4190 case IDBCursor::PREV:
4191 case IDBCursor::PREV_UNIQUE:
4192 AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause);
4193 AppendConditionClause(keyValue, currentKey, true, true,
4194 continueToKeyRangeClause);
4195 if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
4196 AppendConditionClause(keyValue, rangeKey, false,
4197 !mKeyRange->IsLowerOpen(), keyRangeClause);
4198 AppendConditionClause(keyValue, rangeKey, false,
4199 !mKeyRange->IsLowerOpen(),
4200 continueToKeyRangeClause);
4201 mRangeKey = mKeyRange->Lower();
4202 }
4203 break;
4205 default:
4206 MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
4207 }
4209 mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit;
4210 mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause +
4211 openLimit;
4213 return NS_OK;
4214 }
4216 nsresult
4217 OpenKeyCursorHelper::EnsureCursor()
4218 {
4219 MOZ_ASSERT(NS_IsMainThread());
4221 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4222 "OpenKeyCursorHelper::EnsureCursor "
4223 "[IDBObjectStore.cpp]");
4225 if (mCursor || mKey.IsUnset()) {
4226 return NS_OK;
4227 }
4229 mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
4230 mRangeKey, mContinueQuery, mContinueToQuery,
4231 mKey);
4232 IDB_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4234 return NS_OK;
4235 }
4237 nsresult
4238 OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx,
4239 JS::MutableHandle<JS::Value> aVal)
4240 {
4241 MOZ_ASSERT(NS_IsMainThread());
4243 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4244 "OpenKeyCursorHelper::GetSuccessResult "
4245 "[IDBObjectStore.cpp]");
4247 nsresult rv = EnsureCursor();
4248 NS_ENSURE_SUCCESS(rv, rv);
4250 if (mCursor) {
4251 rv = WrapNative(aCx, mCursor, aVal);
4252 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4253 }
4254 else {
4255 aVal.setUndefined();
4256 }
4258 return NS_OK;
4259 }
4261 void
4262 OpenKeyCursorHelper::ReleaseMainThreadObjects()
4263 {
4264 MOZ_ASSERT(NS_IsMainThread());
4266 mKeyRange = nullptr;
4267 mCursor = nullptr;
4269 ObjectStoreHelper::ReleaseMainThreadObjects();
4270 }
4272 nsresult
4273 OpenKeyCursorHelper::PackArgumentsForParentProcess(
4274 ObjectStoreRequestParams& aParams)
4275 {
4276 MOZ_ASSERT(NS_IsMainThread());
4277 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
4279 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4280 "OpenKeyCursorHelper::"
4281 "PackArgumentsForParentProcess "
4282 "[IDBObjectStore.cpp]");
4284 OpenKeyCursorParams params;
4286 if (mKeyRange) {
4287 KeyRange keyRange;
4288 mKeyRange->ToSerializedKeyRange(keyRange);
4289 params.optionalKeyRange() = keyRange;
4290 }
4291 else {
4292 params.optionalKeyRange() = mozilla::void_t();
4293 }
4295 params.direction() = mDirection;
4297 aParams = params;
4298 return NS_OK;
4299 }
4301 AsyncConnectionHelper::ChildProcessSendResult
4302 OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode)
4303 {
4304 MOZ_ASSERT(NS_IsMainThread());
4305 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4306 MOZ_ASSERT(!mCursor);
4308 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4309 "OpenKeyCursorHelper::SendResponseToChildProcess "
4310 "[IDBObjectStore.cpp]");
4312 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
4313 MOZ_ASSERT(actor);
4315 if (NS_SUCCEEDED(aResultCode)) {
4316 nsresult rv = EnsureCursor();
4317 if (NS_FAILED(rv)) {
4318 NS_WARNING("EnsureCursor failed!");
4319 aResultCode = rv;
4320 }
4321 }
4323 ResponseValue response;
4324 if (NS_FAILED(aResultCode)) {
4325 response = aResultCode;
4326 } else {
4327 OpenCursorResponse openCursorResponse;
4329 if (!mCursor) {
4330 openCursorResponse = mozilla::void_t();
4331 }
4332 else {
4333 IndexedDBObjectStoreParent* objectStoreActor =
4334 mObjectStore->GetActorParent();
4335 MOZ_ASSERT(objectStoreActor);
4337 IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent();
4338 MOZ_ASSERT(requestActor);
4340 ObjectStoreCursorConstructorParams params;
4341 params.requestParent() = requestActor;
4342 params.direction() = mDirection;
4343 params.key() = mKey;
4344 params.optionalCloneInfo() = mozilla::void_t();
4346 if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) {
4347 return Error;
4348 }
4349 }
4351 response = openCursorResponse;
4352 }
4354 if (!actor->SendResponse(response)) {
4355 return Error;
4356 }
4358 return Success_Sent;
4359 }
4361 nsresult
4362 OpenKeyCursorHelper::UnpackResponseFromParentProcess(
4363 const ResponseValue& aResponseValue)
4364 {
4365 MOZ_ASSERT(NS_IsMainThread());
4366 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
4367 MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse);
4368 MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() ==
4369 OpenCursorResponse::Tvoid_t ||
4370 aResponseValue.get_OpenCursorResponse().type() ==
4371 OpenCursorResponse::TPIndexedDBCursorChild);
4372 MOZ_ASSERT(!mCursor);
4374 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4375 "OpenKeyCursorHelper::"
4376 "UnpackResponseFromParentProcess "
4377 "[IDBObjectStore.cpp]");
4379 const OpenCursorResponse& response =
4380 aResponseValue.get_OpenCursorResponse();
4382 switch (response.type()) {
4383 case OpenCursorResponse::Tvoid_t:
4384 break;
4386 case OpenCursorResponse::TPIndexedDBCursorChild: {
4387 IndexedDBCursorChild* actor =
4388 static_cast<IndexedDBCursorChild*>(
4389 response.get_PIndexedDBCursorChild());
4391 mCursor = actor->ForgetStrongCursor();
4392 NS_ASSERTION(mCursor, "This should never be null!");
4394 } break;
4396 default:
4397 MOZ_CRASH("Unknown response union type!");
4398 }
4400 return NS_OK;
4401 }
4403 nsresult
4404 CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
4405 {
4406 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4407 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4409 PROFILER_LABEL("IndexedDB", "CreateIndexHelper::DoDatabaseWork");
4411 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
4412 NS_WARNING("Refusing to create index because disk space is low!");
4413 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
4414 }
4416 // Insert the data into the database.
4417 nsCOMPtr<mozIStorageStatement> stmt =
4418 mTransaction->GetCachedStatement(
4419 "INSERT INTO object_store_index (id, name, key_path, unique_index, "
4420 "multientry, object_store_id) "
4421 "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)"
4422 );
4423 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4425 mozStorageStatementScoper scoper(stmt);
4427 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
4428 mIndex->Id());
4429 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4431 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name());
4432 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4434 nsAutoString keyPathSerialization;
4435 mIndex->GetKeyPath().SerializeToString(keyPathSerialization);
4436 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
4437 keyPathSerialization);
4438 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4440 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"),
4441 mIndex->IsUnique() ? 1 : 0);
4442 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4444 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"),
4445 mIndex->IsMultiEntry() ? 1 : 0);
4446 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4448 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
4449 mIndex->ObjectStore()->Id());
4450 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4452 if (NS_FAILED(stmt->Execute())) {
4453 return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
4454 }
4456 #ifdef DEBUG
4457 {
4458 int64_t id;
4459 aConnection->GetLastInsertRowID(&id);
4460 NS_ASSERTION(mIndex->Id() == id, "Bad index id!");
4461 }
4462 #endif
4464 // Now we need to populate the index with data from the object store.
4465 rv = InsertDataFromObjectStore(aConnection);
4466 if (NS_FAILED(rv)) {
4467 return rv;
4468 }
4470 return NS_OK;
4471 }
4473 void
4474 CreateIndexHelper::ReleaseMainThreadObjects()
4475 {
4476 mIndex = nullptr;
4477 NoRequestObjectStoreHelper::ReleaseMainThreadObjects();
4478 }
4480 nsresult
4481 CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
4482 {
4483 nsCOMPtr<mozIStorageStatement> stmt =
4484 mTransaction->GetCachedStatement(
4485 NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM "
4486 "object_data WHERE object_store_id = :osid"));
4487 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4489 mozStorageStatementScoper scoper(stmt);
4491 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
4492 mIndex->ObjectStore()->Id());
4493 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4495 IDB_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX,
4496 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4498 bool hasResult;
4499 rv = stmt->ExecuteStep(&hasResult);
4500 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4501 if (!hasResult) {
4502 // Bail early if we have no data to avoid creating the below runtime
4503 return NS_OK;
4504 }
4506 ThreadLocalJSRuntime* tlsEntry =
4507 reinterpret_cast<ThreadLocalJSRuntime*>(PR_GetThreadPrivate(sTLSIndex));
4509 if (!tlsEntry) {
4510 tlsEntry = ThreadLocalJSRuntime::Create();
4511 IDB_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4513 PR_SetThreadPrivate(sTLSIndex, tlsEntry);
4514 }
4516 JSContext* cx = tlsEntry->Context();
4517 JSAutoRequest ar(cx);
4518 JSAutoCompartment ac(cx, tlsEntry->Global());
4520 do {
4521 StructuredCloneReadInfo cloneReadInfo;
4522 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
4523 mDatabase, cloneReadInfo);
4524 NS_ENSURE_SUCCESS(rv, rv);
4526 JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
4528 JSStructuredCloneCallbacks callbacks = {
4529 IDBObjectStore::StructuredCloneReadCallback<CreateIndexDeserializationTraits>,
4530 nullptr,
4531 nullptr,
4532 nullptr,
4533 nullptr,
4534 nullptr
4535 };
4537 JS::Rooted<JS::Value> clone(cx);
4538 if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) {
4539 NS_WARNING("Failed to deserialize structured clone data!");
4540 return NS_ERROR_DOM_DATA_CLONE_ERR;
4541 }
4543 nsTArray<IndexUpdateInfo> updateInfo;
4544 rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(),
4545 mIndex->GetKeyPath(),
4546 mIndex->IsUnique(),
4547 mIndex->IsMultiEntry(),
4548 tlsEntry->Context(),
4549 clone, updateInfo);
4550 NS_ENSURE_SUCCESS(rv, rv);
4552 int64_t objectDataID = stmt->AsInt64(0);
4554 Key key;
4555 rv = key.SetFromStatement(stmt, 3);
4556 NS_ENSURE_SUCCESS(rv, rv);
4558 rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(),
4559 key, false, objectDataID, updateInfo);
4560 NS_ENSURE_SUCCESS(rv, rv);
4562 } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult);
4563 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4565 return NS_OK;
4566 }
4568 void
4569 CreateIndexHelper::DestroyTLSEntry(void* aPtr)
4570 {
4571 delete reinterpret_cast<ThreadLocalJSRuntime *>(aPtr);
4572 }
4574 nsresult
4575 DeleteIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
4576 {
4577 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4578 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4580 PROFILER_LABEL("IndexedDB", "DeleteIndexHelper::DoDatabaseWork");
4582 nsCOMPtr<mozIStorageStatement> stmt =
4583 mTransaction->GetCachedStatement(
4584 "DELETE FROM object_store_index "
4585 "WHERE name = :name "
4586 );
4587 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4589 mozStorageStatementScoper scoper(stmt);
4591 nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName);
4592 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4594 if (NS_FAILED(stmt->Execute())) {
4595 return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
4596 }
4598 return NS_OK;
4599 }
4601 nsresult
4602 GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
4603 {
4604 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
4605 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4607 PROFILER_LABEL("IndexedDB",
4608 "GetAllHelper::DoDatabaseWork [IDBObjectStore.cpp]");
4610 NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
4611 NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
4613 nsAutoCString keyRangeClause;
4614 if (mKeyRange) {
4615 if (!mKeyRange->Lower().IsUnset()) {
4616 keyRangeClause = NS_LITERAL_CSTRING(" AND key_value");
4617 if (mKeyRange->IsLowerOpen()) {
4618 keyRangeClause.AppendLiteral(" > :");
4619 }
4620 else {
4621 keyRangeClause.AppendLiteral(" >= :");
4622 }
4623 keyRangeClause.Append(lowerKeyName);
4624 }
4626 if (!mKeyRange->Upper().IsUnset()) {
4627 keyRangeClause += NS_LITERAL_CSTRING(" AND key_value");
4628 if (mKeyRange->IsUpperOpen()) {
4629 keyRangeClause.AppendLiteral(" < :");
4630 }
4631 else {
4632 keyRangeClause.AppendLiteral(" <= :");
4633 }
4634 keyRangeClause.Append(upperKeyName);
4635 }
4636 }
4638 nsAutoCString limitClause;
4639 if (mLimit != UINT32_MAX) {
4640 limitClause.AssignLiteral(" LIMIT ");
4641 limitClause.AppendInt(mLimit);
4642 }
4644 nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
4645 "WHERE object_store_id = :osid") +
4646 keyRangeClause +
4647 NS_LITERAL_CSTRING(" ORDER BY key_value ASC") +
4648 limitClause;
4650 mCloneReadInfos.SetCapacity(50);
4652 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
4653 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4655 mozStorageStatementScoper scoper(stmt);
4657 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
4658 mObjectStore->Id());
4659 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4661 if (mKeyRange) {
4662 if (!mKeyRange->Lower().IsUnset()) {
4663 rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
4664 NS_ENSURE_SUCCESS(rv, rv);
4665 }
4666 if (!mKeyRange->Upper().IsUnset()) {
4667 rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
4668 NS_ENSURE_SUCCESS(rv, rv);
4669 }
4670 }
4672 bool hasResult;
4673 while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
4674 if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
4675 mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
4676 }
4678 StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
4679 NS_ASSERTION(readInfo, "Shouldn't fail since SetCapacity succeeded!");
4681 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
4682 mDatabase, *readInfo);
4683 NS_ENSURE_SUCCESS(rv, rv);
4684 }
4685 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4687 return NS_OK;
4688 }
4690 nsresult
4691 GetAllHelper::GetSuccessResult(JSContext* aCx,
4692 JS::MutableHandle<JS::Value> aVal)
4693 {
4694 NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!");
4696 nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal);
4698 NS_ASSERTION(mCloneReadInfos.IsEmpty(),
4699 "Should have cleared in ConvertToArrayAndCleanup");
4700 NS_ENSURE_SUCCESS(rv, rv);
4702 return NS_OK;
4703 }
4705 void
4706 GetAllHelper::ReleaseMainThreadObjects()
4707 {
4708 mKeyRange = nullptr;
4709 for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) {
4710 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]);
4711 }
4712 ObjectStoreHelper::ReleaseMainThreadObjects();
4713 }
4715 nsresult
4716 GetAllHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
4717 {
4718 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4719 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4721 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4722 "GetAllHelper::PackArgumentsForParentProcess "
4723 "[IDBObjectStore.cpp]");
4725 GetAllParams params;
4727 if (mKeyRange) {
4728 KeyRange keyRange;
4729 mKeyRange->ToSerializedKeyRange(keyRange);
4730 params.optionalKeyRange() = keyRange;
4731 }
4732 else {
4733 params.optionalKeyRange() = mozilla::void_t();
4734 }
4736 params.limit() = mLimit;
4738 aParams = params;
4739 return NS_OK;
4740 }
4742 AsyncConnectionHelper::ChildProcessSendResult
4743 GetAllHelper::SendResponseToChildProcess(nsresult aResultCode)
4744 {
4745 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4746 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
4748 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4749 "GetAllHelper::SendResponseToChildProcess "
4750 "[IDBObjectStore.cpp]");
4752 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
4753 NS_ASSERTION(actor, "How did we get this far without an actor?");
4755 GetAllResponse getAllResponse;
4756 if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) {
4757 IDBDatabase* database = mObjectStore->Transaction()->Database();
4758 NS_ASSERTION(database, "This should never be null!");
4760 ContentParent* contentParent = database->GetContentParent();
4761 NS_ASSERTION(contentParent, "This should never be null!");
4763 FileManager* fileManager = database->Manager();
4764 NS_ASSERTION(fileManager, "This should never be null!");
4766 uint32_t length = mCloneReadInfos.Length();
4768 InfallibleTArray<SerializedStructuredCloneReadInfo>& infos =
4769 getAllResponse.cloneInfos();
4770 infos.SetCapacity(length);
4772 InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
4773 blobArrays.SetCapacity(length);
4775 for (uint32_t index = 0;
4776 NS_SUCCEEDED(aResultCode) && index < length;
4777 index++) {
4778 // Append the structured clone data.
4779 const StructuredCloneReadInfo& clone = mCloneReadInfos[index];
4780 SerializedStructuredCloneReadInfo* info = infos.AppendElement();
4781 *info = clone;
4783 // Now take care of the files.
4784 const nsTArray<StructuredCloneFile>& files = clone.mFiles;
4785 BlobArray* blobArray = blobArrays.AppendElement();
4786 InfallibleTArray<PBlobParent*>& blobs = blobArray->blobsParent();
4788 aResultCode =
4789 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
4790 blobs);
4791 if (NS_FAILED(aResultCode)) {
4792 NS_WARNING("ConvertBlobsToActors failed!");
4793 break;
4794 }
4795 }
4796 }
4798 ResponseValue response;
4799 if (NS_FAILED(aResultCode)) {
4800 response = aResultCode;
4801 }
4802 else {
4803 response = getAllResponse;
4804 }
4806 if (!actor->SendResponse(response)) {
4807 return Error;
4808 }
4810 return Success_Sent;
4811 }
4813 nsresult
4814 GetAllHelper::UnpackResponseFromParentProcess(
4815 const ResponseValue& aResponseValue)
4816 {
4817 NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse,
4818 "Bad response type!");
4820 const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse();
4821 const InfallibleTArray<SerializedStructuredCloneReadInfo>& cloneInfos =
4822 getAllResponse.cloneInfos();
4823 const InfallibleTArray<BlobArray>& blobArrays = getAllResponse.blobs();
4825 mCloneReadInfos.SetCapacity(cloneInfos.Length());
4827 for (uint32_t index = 0; index < cloneInfos.Length(); index++) {
4828 const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index];
4829 const InfallibleTArray<PBlobChild*>& blobs = blobArrays[index].blobsChild();
4831 StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement();
4832 if (!destInfo->SetFromSerialized(srcInfo)) {
4833 IDB_WARNING("Failed to copy clone buffer!");
4834 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4835 }
4837 IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles);
4838 }
4840 return NS_OK;
4841 }
4843 nsresult
4844 GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
4845 {
4846 MOZ_ASSERT(!NS_IsMainThread());
4847 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4849 PROFILER_LABEL("IndexedDB",
4850 "GetAllKeysHelper::DoDatabaseWork [IDObjectStore.cpp]");
4852 NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
4854 nsAutoCString keyRangeClause;
4855 if (mKeyRange) {
4856 mKeyRange->GetBindingClause(keyValue, keyRangeClause);
4857 }
4859 nsAutoCString limitClause;
4860 if (mLimit != UINT32_MAX) {
4861 limitClause = NS_LITERAL_CSTRING(" LIMIT ");
4862 limitClause.AppendInt(mLimit);
4863 }
4865 NS_NAMED_LITERAL_CSTRING(osid, "osid");
4867 nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyValue +
4868 NS_LITERAL_CSTRING(" FROM object_data WHERE "
4869 "object_store_id = :") +
4870 osid + keyRangeClause +
4871 NS_LITERAL_CSTRING(" ORDER BY key_value ASC") +
4872 limitClause;
4874 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
4875 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4877 mozStorageStatementScoper scoper(stmt);
4879 nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id());
4880 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4882 if (mKeyRange) {
4883 rv = mKeyRange->BindToStatement(stmt);
4884 NS_ENSURE_SUCCESS(rv, rv);
4885 }
4887 mKeys.SetCapacity(std::min<uint32_t>(50, mLimit));
4889 bool hasResult;
4890 while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
4891 if (mKeys.Capacity() == mKeys.Length()) {
4892 mKeys.SetCapacity(mKeys.Capacity() * 2);
4893 }
4895 Key* key = mKeys.AppendElement();
4896 NS_ASSERTION(key, "This shouldn't fail!");
4898 rv = key->SetFromStatement(stmt, 0);
4899 NS_ENSURE_SUCCESS(rv, rv);
4900 }
4901 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
4903 return NS_OK;
4904 }
4906 nsresult
4907 GetAllKeysHelper::GetSuccessResult(JSContext* aCx,
4908 JS::MutableHandle<JS::Value> aVal)
4909 {
4910 MOZ_ASSERT(NS_IsMainThread());
4911 MOZ_ASSERT(mKeys.Length() <= mLimit);
4913 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4914 "GetAllKeysHelper::GetSuccessResult "
4915 "[IDBObjectStore.cpp]");
4917 nsTArray<Key> keys;
4918 mKeys.SwapElements(keys);
4920 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
4921 if (!array) {
4922 IDB_WARNING("Failed to make array!");
4923 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4924 }
4926 if (!keys.IsEmpty()) {
4927 if (!JS_SetArrayLength(aCx, array, keys.Length())) {
4928 IDB_WARNING("Failed to set array length!");
4929 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4930 }
4932 for (uint32_t index = 0, count = keys.Length(); index < count; index++) {
4933 const Key& key = keys[index];
4934 MOZ_ASSERT(!key.IsUnset());
4936 JS::Rooted<JS::Value> value(aCx);
4937 nsresult rv = key.ToJSVal(aCx, &value);
4938 if (NS_FAILED(rv)) {
4939 NS_WARNING("Failed to get jsval for key!");
4940 return rv;
4941 }
4943 if (!JS_SetElement(aCx, array, index, value)) {
4944 IDB_WARNING("Failed to set array element!");
4945 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
4946 }
4947 }
4948 }
4950 aVal.setObject(*array);
4951 return NS_OK;
4952 }
4954 void
4955 GetAllKeysHelper::ReleaseMainThreadObjects()
4956 {
4957 MOZ_ASSERT(NS_IsMainThread());
4959 mKeyRange = nullptr;
4961 ObjectStoreHelper::ReleaseMainThreadObjects();
4962 }
4964 nsresult
4965 GetAllKeysHelper::PackArgumentsForParentProcess(
4966 ObjectStoreRequestParams& aParams)
4967 {
4968 MOZ_ASSERT(NS_IsMainThread());
4969 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
4971 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4972 "GetAllKeysHelper::PackArgumentsForParentProcess "
4973 "[IDBObjectStore.cpp]");
4975 GetAllKeysParams params;
4977 if (mKeyRange) {
4978 KeyRange keyRange;
4979 mKeyRange->ToSerializedKeyRange(keyRange);
4980 params.optionalKeyRange() = keyRange;
4981 } else {
4982 params.optionalKeyRange() = mozilla::void_t();
4983 }
4985 params.limit() = mLimit;
4987 aParams = params;
4988 return NS_OK;
4989 }
4991 AsyncConnectionHelper::ChildProcessSendResult
4992 GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode)
4993 {
4994 MOZ_ASSERT(NS_IsMainThread());
4995 MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
4997 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
4998 "GetAllKeysHelper::SendResponseToChildProcess "
4999 "[IDBObjectStore.cpp]");
5001 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
5002 MOZ_ASSERT(actor);
5004 ResponseValue response;
5005 if (NS_FAILED(aResultCode)) {
5006 response = aResultCode;
5007 }
5008 else {
5009 GetAllKeysResponse getAllKeysResponse;
5010 getAllKeysResponse.keys().AppendElements(mKeys);
5011 response = getAllKeysResponse;
5012 }
5014 if (!actor->SendResponse(response)) {
5015 return Error;
5016 }
5018 return Success_Sent;
5019 }
5021 nsresult
5022 GetAllKeysHelper::UnpackResponseFromParentProcess(
5023 const ResponseValue& aResponseValue)
5024 {
5025 MOZ_ASSERT(NS_IsMainThread());
5026 MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess());
5027 MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse);
5029 mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys());
5030 return NS_OK;
5031 }
5033 nsresult
5034 CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
5035 {
5036 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
5037 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
5039 PROFILER_LABEL("IndexedDB",
5040 "CountHelper::DoDatabaseWork [IDBObjectStore.cpp]");
5042 NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
5043 NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
5045 nsAutoCString keyRangeClause;
5046 if (mKeyRange) {
5047 if (!mKeyRange->Lower().IsUnset()) {
5048 keyRangeClause = NS_LITERAL_CSTRING(" AND key_value");
5049 if (mKeyRange->IsLowerOpen()) {
5050 keyRangeClause.AppendLiteral(" > :");
5051 }
5052 else {
5053 keyRangeClause.AppendLiteral(" >= :");
5054 }
5055 keyRangeClause.Append(lowerKeyName);
5056 }
5058 if (!mKeyRange->Upper().IsUnset()) {
5059 keyRangeClause += NS_LITERAL_CSTRING(" AND key_value");
5060 if (mKeyRange->IsUpperOpen()) {
5061 keyRangeClause.AppendLiteral(" < :");
5062 }
5063 else {
5064 keyRangeClause.AppendLiteral(" <= :");
5065 }
5066 keyRangeClause.Append(upperKeyName);
5067 }
5068 }
5070 nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data "
5071 "WHERE object_store_id = :osid") +
5072 keyRangeClause;
5074 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
5075 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5077 mozStorageStatementScoper scoper(stmt);
5079 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
5080 mObjectStore->Id());
5081 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5083 if (mKeyRange) {
5084 if (!mKeyRange->Lower().IsUnset()) {
5085 rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
5086 NS_ENSURE_SUCCESS(rv, rv);
5087 }
5088 if (!mKeyRange->Upper().IsUnset()) {
5089 rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
5090 NS_ENSURE_SUCCESS(rv, rv);
5091 }
5092 }
5094 bool hasResult;
5095 rv = stmt->ExecuteStep(&hasResult);
5096 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5097 IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
5099 mCount = stmt->AsInt64(0);
5100 return NS_OK;
5101 }
5103 nsresult
5104 CountHelper::GetSuccessResult(JSContext* aCx,
5105 JS::MutableHandle<JS::Value> aVal)
5106 {
5107 aVal.setNumber(static_cast<double>(mCount));
5108 return NS_OK;
5109 }
5111 void
5112 CountHelper::ReleaseMainThreadObjects()
5113 {
5114 mKeyRange = nullptr;
5115 ObjectStoreHelper::ReleaseMainThreadObjects();
5116 }
5118 nsresult
5119 CountHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams)
5120 {
5121 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
5122 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
5124 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
5125 "CountHelper::PackArgumentsForParentProcess "
5126 "[IDBObjectStore.cpp]");
5128 CountParams params;
5130 if (mKeyRange) {
5131 KeyRange keyRange;
5132 mKeyRange->ToSerializedKeyRange(keyRange);
5133 params.optionalKeyRange() = keyRange;
5134 }
5135 else {
5136 params.optionalKeyRange() = mozilla::void_t();
5137 }
5139 aParams = params;
5140 return NS_OK;
5141 }
5143 AsyncConnectionHelper::ChildProcessSendResult
5144 CountHelper::SendResponseToChildProcess(nsresult aResultCode)
5145 {
5146 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
5147 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
5149 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
5150 "CountHelper::SendResponseToChildProcess "
5151 "[IDBObjectStore.cpp]");
5153 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
5154 NS_ASSERTION(actor, "How did we get this far without an actor?");
5156 ResponseValue response;
5157 if (NS_FAILED(aResultCode)) {
5158 response = aResultCode;
5159 }
5160 else {
5161 CountResponse countResponse = mCount;
5162 response = countResponse;
5163 }
5165 if (!actor->SendResponse(response)) {
5166 return Error;
5167 }
5169 return Success_Sent;
5170 }
5172 nsresult
5173 CountHelper::UnpackResponseFromParentProcess(
5174 const ResponseValue& aResponseValue)
5175 {
5176 NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse,
5177 "Bad response type!");
5179 mCount = aResponseValue.get_CountResponse().count();
5180 return NS_OK;
5181 }