From 46d101f40c605cef3674883009153aa61b1e7f33 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Wed, 17 Sep 2014 19:36:01 -0400 Subject: [PATCH] Backout bug 994190 and merge over some stuff that landed afterwards on a CLOSED TREE. --- content/base/public/nsDOMFile.h | 21 +- content/base/src/nsDOMBlobBuilder.cpp | 23 +- content/base/src/nsDOMBlobBuilder.h | 2 +- content/base/src/nsDOMFile.cpp | 64 +- content/base/src/nsDocument.cpp | 8 + content/base/src/nsFrameMessageManager.cpp | 4 +- dom/archivereader/ArchiveZipFile.cpp | 11 +- dom/archivereader/ArchiveZipFile.h | 6 +- dom/base/nsDOMWindowUtils.cpp | 68 +- dom/base/nsGlobalWindow.cpp | 14 +- dom/bluetooth/BluetoothService.cpp | 2 - dom/bluetooth/BluetoothService.h | 7 +- .../bluedroid/BluetoothOppManager.cpp | 1 - dom/bluetooth/bluedroid/BluetoothOppManager.h | 8 +- dom/bluetooth/bluez/BluetoothDBusService.cpp | 1 - dom/bluetooth/bluez/BluetoothOppManager.cpp | 1 - dom/bluetooth/bluez/BluetoothOppManager.h | 8 +- .../ipc/BluetoothServiceChildProcess.cpp | 1 - dom/bluetooth2/BluetoothService.cpp | 2 - dom/bluetooth2/BluetoothService.h | 7 +- .../bluedroid/BluetoothOppManager.cpp | 1 - .../bluedroid/BluetoothOppManager.h | 8 +- dom/bluetooth2/bluez/BluetoothOppManager.cpp | 1 - dom/bluetooth2/bluez/BluetoothOppManager.h | 8 +- dom/datastore/DataStoreDB.cpp | 48 +- dom/datastore/DataStoreRevision.cpp | 3 +- dom/datastore/DataStoreService.cpp | 2 - dom/datastore/tests/test_oop_events.html | 4 +- .../DeviceStorageRequestChild.cpp | 2 +- .../DeviceStorageRequestParent.cpp | 2 +- dom/devicestorage/nsDeviceStorage.cpp | 2 +- dom/filehandle/FileStreamWrappers.cpp | 29 +- dom/filehandle/FileStreamWrappers.h | 11 +- dom/filehandle/moz.build | 2 - dom/filesystem/CreateFileTask.cpp | 2 - dom/filesystem/FileSystemTaskBase.cpp | 1 - dom/filesystem/FileSystemTaskBase.h | 2 +- dom/filesystem/GetFileOrDirectoryTask.cpp | 2 - dom/filesystem/RemoveTask.cpp | 2 - dom/indexedDB/ActorsChild.cpp | 2351 --- dom/indexedDB/ActorsChild.h | 627 - dom/indexedDB/ActorsParent.cpp | 17034 ---------------- dom/indexedDB/ActorsParent.h | 67 - dom/indexedDB/AsyncConnectionHelper.cpp | 710 + dom/indexedDB/AsyncConnectionHelper.h | 257 + dom/indexedDB/CheckPermissionsHelper.cpp | 220 + dom/indexedDB/CheckPermissionsHelper.h | 59 + dom/indexedDB/Client.cpp | 369 + dom/indexedDB/Client.h | 97 + dom/indexedDB/DatabaseInfo.cpp | 269 + dom/indexedDB/DatabaseInfo.h | 203 + dom/indexedDB/FileInfo.cpp | 174 +- dom/indexedDB/FileInfo.h | 162 +- dom/indexedDB/FileManager.cpp | 433 + dom/indexedDB/FileManager.h | 173 +- dom/indexedDB/FileSnapshot.cpp | 118 +- dom/indexedDB/FileSnapshot.h | 81 +- dom/indexedDB/IDBCursor.cpp | 1377 +- dom/indexedDB/IDBCursor.h | 310 +- dom/indexedDB/IDBDatabase.cpp | 1769 +- dom/indexedDB/IDBDatabase.h | 438 +- dom/indexedDB/IDBEvents.cpp | 114 +- dom/indexedDB/IDBEvents.h | 171 +- dom/indexedDB/IDBFactory.cpp | 1277 +- dom/indexedDB/IDBFactory.h | 296 +- dom/indexedDB/IDBFileHandle.cpp | 12 +- dom/indexedDB/IDBIndex.cpp | 2806 ++- dom/indexedDB/IDBIndex.h | 352 +- dom/indexedDB/IDBKeyRange.cpp | 192 +- dom/indexedDB/IDBKeyRange.h | 226 +- dom/indexedDB/IDBMutableFile.cpp | 305 +- dom/indexedDB/IDBMutableFile.h | 82 +- dom/indexedDB/IDBObjectStore.cpp | 5893 ++++-- dom/indexedDB/IDBObjectStore.h | 482 +- dom/indexedDB/IDBRequest.cpp | 413 +- dom/indexedDB/IDBRequest.h | 236 +- dom/indexedDB/IDBTransaction.cpp | 1749 +- dom/indexedDB/IDBTransaction.h | 570 +- dom/indexedDB/IDBWrapperCache.cpp | 26 +- dom/indexedDB/IDBWrapperCache.h | 46 +- dom/indexedDB/IndexedDatabase.h | 160 +- dom/indexedDB/IndexedDatabaseInlines.h | 103 +- dom/indexedDB/IndexedDatabaseManager.cpp | 91 +- dom/indexedDB/IndexedDatabaseManager.h | 23 +- dom/indexedDB/Key.cpp | 138 +- dom/indexedDB/Key.h | 285 +- dom/indexedDB/KeyPath.cpp | 13 +- dom/indexedDB/KeyPath.h | 24 +- dom/indexedDB/OpenDatabaseHelper.cpp | 2865 +++ dom/indexedDB/OpenDatabaseHelper.h | 175 + dom/indexedDB/PBackgroundIDBCursor.ipdl | 90 - dom/indexedDB/PBackgroundIDBDatabase.ipdl | 72 - dom/indexedDB/PBackgroundIDBFactory.ipdl | 63 - .../PBackgroundIDBFactoryRequest.ipdl | 48 - dom/indexedDB/PBackgroundIDBRequest.ipdl | 112 - dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh | 259 - dom/indexedDB/PBackgroundIDBTransaction.ipdl | 41 - ...BackgroundIDBVersionChangeTransaction.ipdl | 49 - .../PIndexedDBPermissionRequest.ipdl | 27 - dom/indexedDB/PermissionRequestBase.cpp | 267 - dom/indexedDB/PermissionRequestBase.h | 77 - dom/indexedDB/ProfilerHelpers.h | 8 +- dom/indexedDB/ReportInternalError.cpp | 8 +- dom/indexedDB/ReportInternalError.h | 8 +- dom/indexedDB/SerializationHelpers.h | 103 - dom/indexedDB/TransactionThreadPool.cpp | 851 +- dom/indexedDB/TransactionThreadPool.h | 238 +- dom/indexedDB/ipc/IndexedDBChild.cpp | 1401 ++ dom/indexedDB/ipc/IndexedDBChild.h | 457 + dom/indexedDB/ipc/IndexedDBParams.ipdlh | 76 + dom/indexedDB/ipc/IndexedDBParent.cpp | 2262 ++ dom/indexedDB/ipc/IndexedDBParent.h | 880 + dom/indexedDB/ipc/Makefile.in | 14 + dom/indexedDB/ipc/PIndexedDB.ipdl | 37 + dom/indexedDB/ipc/PIndexedDBCursor.ipdl | 49 + dom/indexedDB/ipc/PIndexedDBDatabase.ipdl | 69 + .../PIndexedDBDeleteDatabaseRequest.ipdl} | 12 +- dom/indexedDB/ipc/PIndexedDBIndex.ipdl | 64 + dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl | 113 + dom/indexedDB/ipc/PIndexedDBRequest.ipdl | 113 + dom/indexedDB/ipc/PIndexedDBTransaction.ipdl | 73 + dom/indexedDB/ipc/SerializationHelpers.h | 309 + dom/indexedDB/ipc/mochitest.ini | 4 + dom/indexedDB/ipc/moz.build | 41 + dom/indexedDB/ipc/unit/head.js | 18 + dom/indexedDB/ipc/unit/xpcshell.ini | 68 + dom/indexedDB/moz.build | 58 +- dom/indexedDB/test/browser.ini | 52 +- dom/indexedDB/test/file.js | 39 +- dom/indexedDB/test/helpers.js | 62 +- dom/indexedDB/test/mochitest.ini | 341 +- dom/indexedDB/test/test_blob_simple.html | 17 - dom/indexedDB/test/test_blocked_order.html | 18 - .../test/test_disabled_quota_prompt.html | 118 - .../test_file_cross_database_copying.html | 6 +- dom/indexedDB/test/test_file_os_delete.html | 7 - dom/indexedDB/test/test_file_sharing.html | 6 +- .../test/test_filehandle_compat.html | 2 +- .../test/test_filehandle_getFile.html | 2 +- .../test/test_filehandle_lifetimes.html | 2 +- .../test_filehandle_lifetimes_nested.html | 2 +- .../test/test_filehandle_location.html | 2 +- .../test/test_filehandle_ordering.html | 2 +- .../test/test_filehandle_overlapping.html | 2 +- .../test_filehandle_readonly_exceptions.html | 2 +- .../test_filehandle_request_readyState.html | 2 +- ...filehandle_success_events_after_abort.html | 2 +- dom/indexedDB/test/test_invalidate.html | 18 - dom/indexedDB/test/test_persistenceType.html | 13 +- ...pcshell-head-parent-process.js => head.js} | 85 +- .../{xpcshell-shared.ini => mochitest.ini} | 11 +- dom/indexedDB/test/unit/test_blocked_order.js | 150 - dom/indexedDB/test/unit/test_indexes.js | 2 +- .../test/unit/test_indexes_funny_things.js | 2 +- dom/indexedDB/test/unit/test_invalidate.js | 82 - .../test/unit/test_setVersion_events.js | 4 +- .../test/unit/test_temporary_storage.js | 32 +- .../test/unit/xpcshell-child-process.ini | 18 - .../test/unit/xpcshell-head-child-process.js | 27 - .../test/unit/xpcshell-parent-process.ini | 26 - dom/indexedDB/test/unit/xpcshell.ini | 90 + dom/interfaces/base/nsIDOMWindowUtils.idl | 9 +- dom/ipc/Blob.cpp | 1818 +- dom/ipc/Blob.h | 200 + dom/ipc/BlobChild.h | 184 - dom/ipc/BlobParent.h | 216 - dom/ipc/ContentBridgeChild.cpp | 2 +- dom/ipc/ContentChild.cpp | 18 +- dom/ipc/ContentChild.h | 12 +- dom/ipc/ContentParent.cpp | 113 +- dom/ipc/ContentParent.h | 25 +- .../ipc}/FileDescriptorSetChild.cpp | 8 +- .../glue => dom/ipc}/FileDescriptorSetChild.h | 35 +- .../ipc}/FileDescriptorSetParent.cpp | 8 +- .../ipc}/FileDescriptorSetParent.h | 35 +- dom/ipc/FilePickerParent.cpp | 2 +- dom/ipc/PBlob.ipdl | 14 +- dom/ipc/PBrowser.ipdl | 20 +- dom/ipc/PContent.ipdl | 15 +- {ipc/glue => dom/ipc}/PFileDescriptorSet.ipdl | 7 +- dom/ipc/TabChild.cpp | 43 +- dom/ipc/TabChild.h | 15 +- dom/ipc/TabParent.cpp | 158 +- dom/ipc/TabParent.h | 28 +- dom/ipc/moz.build | 10 +- dom/ipc/nsIContentChild.cpp | 11 +- dom/ipc/nsIContentChild.h | 22 +- dom/ipc/nsIContentParent.cpp | 13 +- dom/ipc/nsIContentParent.h | 18 +- dom/ipc/nsIRemoteBlob.h | 23 +- dom/mobilemessage/MmsMessage.cpp | 2 - dom/mobilemessage/ipc/SmsIPCService.cpp | 1 - dom/mobilemessage/ipc/SmsParent.cpp | 2 +- dom/quota/QuotaManager.cpp | 154 +- dom/quota/QuotaManager.h | 28 +- dom/quota/StoragePrivilege.h | 5 +- dom/quota/nsIOfflineStorage.h | 19 +- dom/webidl/IDBDatabase.webidl | 1 + dom/webidl/IDBIndex.webidl | 8 +- dom/webidl/IDBObjectStore.webidl | 1 + dom/webidl/IDBTransaction.webidl | 1 + ipc/glue/BackgroundChild.h | 7 - ipc/glue/BackgroundChildImpl.cpp | 64 +- ipc/glue/BackgroundChildImpl.h | 39 +- ipc/glue/BackgroundImpl.cpp | 184 +- ipc/glue/BackgroundParent.h | 8 - ipc/glue/BackgroundParentImpl.cpp | 99 +- ipc/glue/BackgroundParentImpl.h | 28 - ipc/glue/InputStreamParams.ipdlh | 9 - ipc/glue/InputStreamUtils.cpp | 48 +- ipc/glue/PBackground.ipdl | 32 +- ipc/glue/moz.build | 5 - netwerk/base/public/nsIFileStreams.idl | 8 - netwerk/base/src/nsFileStreams.cpp | 26 +- netwerk/protocol/http/HttpChannelChild.cpp | 2 +- netwerk/protocol/http/HttpChannelParent.cpp | 2 +- testing/mochitest/b2g_start_script.js | 2 - .../components/SpecialPowersObserver.js | 1 - .../content/SpecialPowersObserverAPI.js | 96 +- .../specialpowers/content/specialpowersAPI.js | 60 +- .../idbcursor_continue_invalid.htm.ini | 5 + .../idbfactory_deleteDatabase3.htm.ini | 5 + .../IndexedDB/idbobjectstore_clear.htm.ini | 8 + .../IndexedDB/idbobjectstore_clear2.htm.ini | 8 + .../IndexedDB/idbversionchangeevent.htm.ini | 5 + widget/android/NativeJSContainer.cpp | 1 - widget/xpwidgets/nsFilePickerProxy.cpp | 2 +- xpcom/io/nsILocalFileWin.idl | 15 +- xpcom/io/nsLocalFileWin.cpp | 49 +- xpcom/io/nsLocalFileWin.h | 5 - xpcom/threads/LazyIdleThread.cpp | 4 - 231 files changed, 26922 insertions(+), 33708 deletions(-) delete mode 100644 dom/indexedDB/ActorsChild.cpp delete mode 100644 dom/indexedDB/ActorsChild.h delete mode 100644 dom/indexedDB/ActorsParent.cpp delete mode 100644 dom/indexedDB/ActorsParent.h create mode 100644 dom/indexedDB/AsyncConnectionHelper.cpp create mode 100644 dom/indexedDB/AsyncConnectionHelper.h create mode 100644 dom/indexedDB/CheckPermissionsHelper.cpp create mode 100644 dom/indexedDB/CheckPermissionsHelper.h create mode 100644 dom/indexedDB/Client.cpp create mode 100644 dom/indexedDB/Client.h create mode 100644 dom/indexedDB/DatabaseInfo.cpp create mode 100644 dom/indexedDB/DatabaseInfo.h create mode 100644 dom/indexedDB/FileManager.cpp create mode 100644 dom/indexedDB/OpenDatabaseHelper.cpp create mode 100644 dom/indexedDB/OpenDatabaseHelper.h delete mode 100644 dom/indexedDB/PBackgroundIDBCursor.ipdl delete mode 100644 dom/indexedDB/PBackgroundIDBDatabase.ipdl delete mode 100644 dom/indexedDB/PBackgroundIDBFactory.ipdl delete mode 100644 dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl delete mode 100644 dom/indexedDB/PBackgroundIDBRequest.ipdl delete mode 100644 dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh delete mode 100644 dom/indexedDB/PBackgroundIDBTransaction.ipdl delete mode 100644 dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl delete mode 100644 dom/indexedDB/PIndexedDBPermissionRequest.ipdl delete mode 100644 dom/indexedDB/PermissionRequestBase.cpp delete mode 100644 dom/indexedDB/PermissionRequestBase.h delete mode 100644 dom/indexedDB/SerializationHelpers.h create mode 100644 dom/indexedDB/ipc/IndexedDBChild.cpp create mode 100644 dom/indexedDB/ipc/IndexedDBChild.h create mode 100644 dom/indexedDB/ipc/IndexedDBParams.ipdlh create mode 100644 dom/indexedDB/ipc/IndexedDBParent.cpp create mode 100644 dom/indexedDB/ipc/IndexedDBParent.h create mode 100644 dom/indexedDB/ipc/Makefile.in create mode 100644 dom/indexedDB/ipc/PIndexedDB.ipdl create mode 100644 dom/indexedDB/ipc/PIndexedDBCursor.ipdl create mode 100644 dom/indexedDB/ipc/PIndexedDBDatabase.ipdl rename dom/indexedDB/{PBackgroundIDBDatabaseFile.ipdl => ipc/PIndexedDBDeleteDatabaseRequest.ipdl} (67%) create mode 100644 dom/indexedDB/ipc/PIndexedDBIndex.ipdl create mode 100644 dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl create mode 100644 dom/indexedDB/ipc/PIndexedDBRequest.ipdl create mode 100644 dom/indexedDB/ipc/PIndexedDBTransaction.ipdl create mode 100644 dom/indexedDB/ipc/SerializationHelpers.h create mode 100644 dom/indexedDB/ipc/mochitest.ini create mode 100644 dom/indexedDB/ipc/moz.build create mode 100644 dom/indexedDB/ipc/unit/head.js create mode 100644 dom/indexedDB/ipc/unit/xpcshell.ini delete mode 100644 dom/indexedDB/test/test_blocked_order.html delete mode 100644 dom/indexedDB/test/test_disabled_quota_prompt.html delete mode 100644 dom/indexedDB/test/test_invalidate.html rename dom/indexedDB/test/unit/{xpcshell-head-parent-process.js => head.js} (74%) rename dom/indexedDB/test/unit/{xpcshell-shared.ini => mochitest.ini} (88%) delete mode 100644 dom/indexedDB/test/unit/test_blocked_order.js delete mode 100644 dom/indexedDB/test/unit/test_invalidate.js delete mode 100644 dom/indexedDB/test/unit/xpcshell-child-process.ini delete mode 100644 dom/indexedDB/test/unit/xpcshell-head-child-process.js delete mode 100644 dom/indexedDB/test/unit/xpcshell-parent-process.ini create mode 100644 dom/indexedDB/test/unit/xpcshell.ini create mode 100644 dom/ipc/Blob.h delete mode 100644 dom/ipc/BlobChild.h delete mode 100644 dom/ipc/BlobParent.h rename {ipc/glue => dom/ipc}/FileDescriptorSetChild.cpp (90%) rename {ipc/glue => dom/ipc}/FileDescriptorSetChild.h (68%) rename {ipc/glue => dom/ipc}/FileDescriptorSetParent.cpp (91%) rename {ipc/glue => dom/ipc}/FileDescriptorSetParent.h (69%) rename {ipc/glue => dom/ipc}/PFileDescriptorSet.ipdl (78%) create mode 100644 testing/web-platform/meta/IndexedDB/idbcursor_continue_invalid.htm.ini create mode 100644 testing/web-platform/meta/IndexedDB/idbfactory_deleteDatabase3.htm.ini create mode 100644 testing/web-platform/meta/IndexedDB/idbobjectstore_clear.htm.ini create mode 100644 testing/web-platform/meta/IndexedDB/idbobjectstore_clear2.htm.ini create mode 100644 testing/web-platform/meta/IndexedDB/idbversionchangeevent.htm.ini diff --git a/content/base/public/nsDOMFile.h b/content/base/public/nsDOMFile.h index c6e3416629c9..f4c91a827f02 100644 --- a/content/base/public/nsDOMFile.h +++ b/content/base/public/nsDOMFile.h @@ -33,7 +33,6 @@ #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "nsWrapperCache.h" #include "nsCycleCollectionParticipant.h" -#include "nsWeakReference.h" class nsDOMMultipartFile; class nsIFile; @@ -65,7 +64,6 @@ class DOMFile MOZ_FINAL : public nsIDOMFile , public nsIXHRSendable , public nsIMutable , public nsIJSNativeInitializer - , public nsSupportsWeakReference { public: NS_DECL_NSIDOMBLOB @@ -192,9 +190,9 @@ public: virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) = 0; nsresult Slice(int64_t aStart, int64_t aEnd, const nsAString& aContentType, - uint8_t aArgc, DOMFileImpl** aBlobImpl); + uint8_t aArgc, nsIDOMBlob **aBlob); - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) = 0; @@ -323,7 +321,7 @@ public: virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) MOZ_OVERRIDE; - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; @@ -466,7 +464,7 @@ public: virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE; - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; @@ -553,7 +551,7 @@ public: virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE; - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; @@ -575,7 +573,7 @@ private: nsString mContentType; }; -class DOMFileImplFile : public DOMFileImplBase +class DOMFileImplFile MOZ_FINAL : public DOMFileImplBase { public: NS_DECL_ISUPPORTS_INHERITED @@ -692,9 +690,6 @@ public: void SetPath(const nsAString& aFullPath); -protected: - virtual ~DOMFileImplFile() {} - private: // Create slice DOMFileImplFile(const DOMFileImplFile* aOther, uint64_t aStart, @@ -724,7 +719,9 @@ private: } } - virtual already_AddRefed + ~DOMFileImplFile() {} + + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp index c8adb79a51b1..33f5fc0733e3 100644 --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -55,7 +55,7 @@ DOMMultipartFileImpl::GetInternalStream(nsIInputStream** aStream) return CallQueryInterface(stream, aStream); } -already_AddRefed +already_AddRefed DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { @@ -77,17 +77,18 @@ DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, if (skipStart < l) { uint64_t upperBound = std::min(l - skipStart, length); - nsRefPtr firstImpl; - rv = blobImpl->Slice(skipStart, skipStart + upperBound, aContentType, 3, - getter_AddRefs(firstImpl)); + nsCOMPtr firstBlob; + rv = blobImpl->Slice(skipStart, skipStart + upperBound, + aContentType, 3, + getter_AddRefs(firstBlob)); NS_ENSURE_SUCCESS(rv, nullptr); // Avoid wrapping a single blob inside an DOMMultipartFileImpl if (length == upperBound) { - return firstImpl.forget(); + return firstBlob.forget(); } - blobImpls.AppendElement(firstImpl); + blobImpls.AppendElement(static_cast(firstBlob.get())->Impl()); length -= upperBound; i++; break; @@ -104,12 +105,12 @@ DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, NS_ENSURE_SUCCESS(rv, nullptr); if (length < l) { - nsRefPtr lastBlob; + nsCOMPtr lastBlob; rv = blobImpl->Slice(0, length, aContentType, 3, getter_AddRefs(lastBlob)); NS_ENSURE_SUCCESS(rv, nullptr); - blobImpls.AppendElement(lastBlob); + blobImpls.AppendElement(static_cast(lastBlob.get())->Impl()); } else { blobImpls.AppendElement(blobImpl); } @@ -117,9 +118,9 @@ DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, } // we can create our blob now - nsRefPtr impl = - new DOMMultipartFileImpl(blobImpls, aContentType); - return impl.forget(); + nsCOMPtr blob = + new DOMFile(new DOMMultipartFileImpl(blobImpls, aContentType)); + return blob.forget(); } /* static */ nsresult diff --git a/content/base/src/nsDOMBlobBuilder.h b/content/base/src/nsDOMBlobBuilder.h index 45bcf232e16b..7f238e442194 100644 --- a/content/base/src/nsDOMBlobBuilder.h +++ b/content/base/src/nsDOMBlobBuilder.h @@ -78,7 +78,7 @@ public: uint32_t aArgc, JS::Value* aArgv); - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 132d9ddf9ee9..1ab34fe4a9ca 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -147,7 +147,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMFile) NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) NS_INTERFACE_MAP_ENTRY(nsIMutable) NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer) - NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, IsFile()) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !(IsFile())) NS_INTERFACE_MAP_END @@ -299,10 +298,7 @@ already_AddRefed DOMFile::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsRefPtr impl = - mImpl->CreateSlice(aStart, aLength, aContentType); - nsRefPtr slice = new DOMFile(impl); - return slice.forget(); + return mImpl->CreateSlice(aStart, aLength, aContentType); } NS_IMETHODIMP @@ -404,17 +400,7 @@ DOMFile::Slice(int64_t aStart, int64_t aEnd, nsIDOMBlob **aBlob) { MOZ_ASSERT(mImpl); - nsRefPtr impl; - nsresult rv = mImpl->Slice(aStart, aEnd, aContentType, aArgc, - getter_AddRefs(impl)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsRefPtr blob = new DOMFile(impl); - blob.forget(aBlob); - - return NS_OK; + return mImpl->Slice(aStart, aEnd, aContentType, aArgc, aBlob); } NS_IMETHODIMP @@ -474,9 +460,9 @@ DOMFile::IsMemoryFile() nsresult DOMFileImpl::Slice(int64_t aStart, int64_t aEnd, const nsAString& aContentType, uint8_t aArgc, - DOMFileImpl** aBlobImpl) + nsIDOMBlob **aBlob) { - *aBlobImpl = nullptr; + *aBlob = nullptr; // Truncate aStart and aEnd so that we stay within this file. uint64_t thisLength; @@ -489,15 +475,12 @@ DOMFileImpl::Slice(int64_t aStart, int64_t aEnd, ParseSize((int64_t)thisLength, aStart, aEnd); - nsRefPtr impl = + // Create the new file + nsCOMPtr blob = CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart), aContentType); - if (!impl) { - return NS_ERROR_UNEXPECTED; - } - - impl.forget(aBlobImpl); - return NS_OK; + blob.forget(aBlob); + return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED; } //////////////////////////////////////////////////////////////////////////// @@ -592,7 +575,7 @@ DOMFileImplBase::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) return NS_OK; } -already_AddRefed +already_AddRefed DOMFileImplBase::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { @@ -741,13 +724,13 @@ DOMFileImplBase::SetMutable(bool aMutable) //////////////////////////////////////////////////////////////////////////// // DOMFileImplFile implementation -already_AddRefed +already_AddRefed DOMFileImplFile::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsRefPtr impl = - new DOMFileImplFile(this, aStart, aLength, aContentType); - return impl.forget(); + nsCOMPtr blob = + new DOMFile(new DOMFileImplFile(this, aStart, aLength, aContentType)); + return blob.forget(); } nsresult @@ -849,8 +832,7 @@ DOMFileImplFile::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) const uint32_t sFileStreamFlags = nsIFileInputStream::CLOSE_ON_EOF | nsIFileInputStream::REOPEN_ON_REWIND | - nsIFileInputStream::DEFER_OPEN | - nsIFileInputStream::SHARE_DELETE; + nsIFileInputStream::DEFER_OPEN; nsresult DOMFileImplFile::GetInternalStream(nsIInputStream** aStream) @@ -875,13 +857,13 @@ DOMFileImplFile::SetPath(const nsAString& aPath) NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplMemory, DOMFileImpl) -already_AddRefed +already_AddRefed DOMFileImplMemory::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsRefPtr impl = - new DOMFileImplMemory(this, aStart, aLength, aContentType); - return impl.forget(); + nsCOMPtr blob = + new DOMFile(new DOMFileImplMemory(this, aStart, aLength, aContentType)); + return blob.forget(); } nsresult @@ -1000,17 +982,17 @@ DOMFileImplMemory::DataOwner::EnsureMemoryReporterRegistered() NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplTemporaryFileBlob, DOMFileImpl) -already_AddRefed +already_AddRefed DOMFileImplTemporaryFileBlob::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { if (aStart + aLength > mLength) return nullptr; - nsRefPtr impl = - new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos, aLength, - aContentType); - return impl.forget(); + nsCOMPtr blob = + new DOMFile(new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos, + aLength, aContentType)); + return blob.forget(); } nsresult diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 31bbb6431e3b..319d0aba2a42 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -153,6 +153,7 @@ #include "mozAutoDocUpdate.h" #include "nsGlobalWindow.h" #include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/quota/QuotaManager.h" #include "nsDOMNavigationTiming.h" #include "nsSMILAnimationController.h" @@ -8509,6 +8510,13 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest) } } + // Check if we have running offline storage transactions + quota::QuotaManager* quotaManager = + win ? quota::QuotaManager::Get() : nullptr; + if (quotaManager && quotaManager->HasOpenTransactions(win)) { + return false; + } + #ifdef MOZ_MEDIA_NAVIGATOR // Check if we have active GetUserMedia use if (MediaManager::Exists() && win && diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index b09e14743dc1..5665d36cffbc 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -34,8 +34,8 @@ #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/StructuredCloneUtils.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/PBlobChild.h" +#include "mozilla/dom/PBlobParent.h" #include "JavaScriptChild.h" #include "JavaScriptParent.h" #include "mozilla/dom/DOMStringList.h" diff --git a/dom/archivereader/ArchiveZipFile.cpp b/dom/archivereader/ArchiveZipFile.cpp index 816bdbf30f0c..c7973729fae6 100644 --- a/dom/archivereader/ArchiveZipFile.cpp +++ b/dom/archivereader/ArchiveZipFile.cpp @@ -396,15 +396,16 @@ ArchiveZipFileImpl::Traverse(nsCycleCollectionTraversalCallback &cb) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArchiveReader); } -already_AddRefed +already_AddRefed ArchiveZipFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsRefPtr impl = - new ArchiveZipFileImpl(mFilename, mContentType, aStart, mLength, mCentral, - mArchiveReader); - return impl.forget(); + nsCOMPtr t = + new DOMFile(new ArchiveZipFileImpl(mFilename, mContentType, + aStart, mLength, mCentral, + mArchiveReader)); + return t.forget(); } NS_IMPL_ISUPPORTS_INHERITED0(ArchiveZipFileImpl, DOMFileImpl) diff --git a/dom/archivereader/ArchiveZipFile.h b/dom/archivereader/ArchiveZipFile.h index dcf74323268a..dfede39fca85 100644 --- a/dom/archivereader/ArchiveZipFile.h +++ b/dom/archivereader/ArchiveZipFile.h @@ -71,9 +71,9 @@ protected: MOZ_COUNT_DTOR(ArchiveZipFileImpl); } - virtual already_AddRefed CreateSlice(uint64_t aStart, - uint64_t aLength, - const nsAString& aContentType) MOZ_OVERRIDE; + virtual already_AddRefed CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType) MOZ_OVERRIDE; private: // Data ZipCentral mCentral; diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index ec4f0ba05455..9eeab72b6198 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -100,7 +100,6 @@ using namespace mozilla; using namespace mozilla::dom; -using namespace mozilla::ipc; using namespace mozilla::layers; using namespace mozilla::widget; using namespace mozilla::gfx; @@ -3005,65 +3004,30 @@ nsDOMWindowUtils::AreDialogsEnabled(bool* aResult) NS_IMETHODIMP nsDOMWindowUtils::GetFileId(JS::Handle aFile, JSContext* aCx, - int64_t* _retval) + int64_t* aResult) { MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - if (aFile.isPrimitive()) { - *_retval = -1; - return NS_OK; - } + if (!aFile.isPrimitive()) { + JSObject* obj = aFile.toObjectOrNull(); - JSObject* obj = aFile.toObjectOrNull(); - - indexedDB::IDBMutableFile* mutableFile = nullptr; - if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) { - *_retval = mutableFile->GetFileId(); - return NS_OK; - } - - nsISupports* nativeObj = - nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); - - nsCOMPtr blob = do_QueryInterface(nativeObj); - if (blob) { - *_retval = blob->GetFileId(); - return NS_OK; - } - - *_retval = -1; - return NS_OK; -} - -NS_IMETHODIMP -nsDOMWindowUtils::GetFilePath(JS::HandleValue aFile, JSContext* aCx, - nsAString& _retval) -{ - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - if (aFile.isPrimitive()) { - _retval.Truncate(); - return NS_OK; - } - - JSObject* obj = aFile.toObjectOrNull(); - - nsISupports* nativeObj = - nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); - - nsCOMPtr file = do_QueryInterface(nativeObj); - if (file) { - nsString filePath; - nsresult rv = file->GetMozFullPathInternal(filePath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + indexedDB::IDBMutableFile* mutableFile = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) { + *aResult = mutableFile->GetFileId(); + return NS_OK; } - _retval = filePath; - return NS_OK; + nsISupports* nativeObj = + nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); + + nsCOMPtr blob = do_QueryInterface(nativeObj); + if (blob) { + *aResult = blob->GetFileId(); + return NS_OK; + } } - _retval.Truncate(); + *aResult = -1; return NS_OK; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index b4dbb24c18da..df5f51713338 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -187,6 +187,7 @@ #include "mozilla/dom/MessagePort.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/indexedDB/IDBFactory.h" +#include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/StructuredCloneTags.h" @@ -1561,6 +1562,12 @@ nsGlobalWindow::FreeInnerObjects() // Kill all of the workers for this window. mozilla::dom::workers::CancelWorkersForWindow(this); + // Close all offline storages for this window. + quota::QuotaManager* quotaManager = quota::QuotaManager::Get(); + if (quotaManager) { + quotaManager->AbortCloseStoragesForWindow(this); + } + ClearAllTimeouts(); if (mIdleTimer) { @@ -10574,11 +10581,9 @@ GetIndexedDBEnabledForAboutURI(nsIURI *aURI) return flags & nsIAboutModule::ENABLE_INDEXED_DB; } -mozilla::dom::indexedDB::IDBFactory* +indexedDB::IDBFactory* nsGlobalWindow::GetIndexedDB(ErrorResult& aError) { - using mozilla::dom::indexedDB::IDBFactory; - if (!mIndexedDB) { // If the document has the sandboxed origin flag set // don't allow access to indexedDB. @@ -10625,7 +10630,8 @@ nsGlobalWindow::GetIndexedDB(ErrorResult& aError) } // This may be null if being created from a file. - aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB)); + aError = indexedDB::IDBFactory::Create(this, nullptr, + getter_AddRefs(mIndexedDB)); } return mIndexedDB; diff --git a/dom/bluetooth/BluetoothService.cpp b/dom/bluetooth/BluetoothService.cpp index ccb2b1b45fa6..4784ae3608b1 100644 --- a/dom/bluetooth/BluetoothService.cpp +++ b/dom/bluetooth/BluetoothService.cpp @@ -26,8 +26,6 @@ #include "mozilla/unused.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/ipc/UnixSocket.h" #include "nsContentUtils.h" #include "nsIObserverService.h" diff --git a/dom/bluetooth/BluetoothService.h b/dom/bluetooth/BluetoothService.h index d2df15a326bd..8a3f006feb9b 100644 --- a/dom/bluetooth/BluetoothService.h +++ b/dom/bluetooth/BluetoothService.h @@ -9,6 +9,7 @@ #include "BluetoothCommon.h" #include "BluetoothProfileManagerBase.h" +#include "mozilla/dom/ipc/Blob.h" #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsIDOMFile.h" @@ -16,13 +17,7 @@ #include "nsTObserverArray.h" #include "nsThreadUtils.h" -class nsIDOMBlob; - namespace mozilla { -namespace dom { -class BlobChild; -class BlobParent; -} namespace ipc { class UnixSocketConsumer; } diff --git a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp index 09ff842f8940..91a9c6d0abf6 100644 --- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp +++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp @@ -14,7 +14,6 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth/bluedroid/BluetoothOppManager.h b/dom/bluetooth/bluedroid/BluetoothOppManager.h index fbfda2f294c6..a0805ac67c17 100644 --- a/dom/bluetooth/bluedroid/BluetoothOppManager.h +++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h @@ -11,20 +11,14 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" +#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" -class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; -namespace mozilla { -namespace dom { -class BlobParent; -} -} - BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/bluetooth/bluez/BluetoothDBusService.cpp b/dom/bluetooth/bluez/BluetoothDBusService.cpp index 838e62941e32..51d07d102bda 100644 --- a/dom/bluetooth/bluez/BluetoothDBusService.cpp +++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp @@ -44,7 +44,6 @@ #include "mozilla/ipc/DBusUtils.h" #include "mozilla/ipc/RawDBusConnection.h" #include "mozilla/LazyIdleThread.h" -#include "mozilla/Monitor.h" #include "mozilla/Mutex.h" #include "mozilla/NullPtr.h" #include "mozilla/StaticMutex.h" diff --git a/dom/bluetooth/bluez/BluetoothOppManager.cpp b/dom/bluetooth/bluez/BluetoothOppManager.cpp index 26351aaea420..19bccd4423eb 100644 --- a/dom/bluetooth/bluez/BluetoothOppManager.cpp +++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp @@ -14,7 +14,6 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth/bluez/BluetoothOppManager.h b/dom/bluetooth/bluez/BluetoothOppManager.h index d6e7bf6daa5c..d7de3917883a 100644 --- a/dom/bluetooth/bluez/BluetoothOppManager.h +++ b/dom/bluetooth/bluez/BluetoothOppManager.h @@ -11,20 +11,14 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" +#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" -class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; -namespace mozilla { -namespace dom { -class BlobParent; -} -} - BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp index 9f5ac93e929b..5c6038191dff 100644 --- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp +++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp @@ -10,7 +10,6 @@ #include "mozilla/Assertions.h" #include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/ipc/BlobChild.h" #include "BluetoothChild.h" #include "MainThreadUtils.h" diff --git a/dom/bluetooth2/BluetoothService.cpp b/dom/bluetooth2/BluetoothService.cpp index efa78cb74e19..428c7c1f25c2 100644 --- a/dom/bluetooth2/BluetoothService.cpp +++ b/dom/bluetooth2/BluetoothService.cpp @@ -26,8 +26,6 @@ #include "mozilla/unused.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "nsContentUtils.h" #include "nsIObserverService.h" #include "nsISettingsService.h" diff --git a/dom/bluetooth2/BluetoothService.h b/dom/bluetooth2/BluetoothService.h index 8d05f49fcda5..0588d15e76c3 100644 --- a/dom/bluetooth2/BluetoothService.h +++ b/dom/bluetooth2/BluetoothService.h @@ -9,6 +9,7 @@ #include "BluetoothCommon.h" #include "BluetoothProfileManagerBase.h" +#include "mozilla/dom/ipc/Blob.h" #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsIDOMFile.h" @@ -16,13 +17,7 @@ #include "nsTObserverArray.h" #include "nsThreadUtils.h" -class nsIDOMBlob; - namespace mozilla { -namespace dom { -class BlobChild; -class BlobParent; -} namespace ipc { class UnixSocketConsumer; } diff --git a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp index 09ff842f8940..91a9c6d0abf6 100644 --- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp +++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp @@ -14,7 +14,6 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth2/bluedroid/BluetoothOppManager.h b/dom/bluetooth2/bluedroid/BluetoothOppManager.h index fbfda2f294c6..a0805ac67c17 100644 --- a/dom/bluetooth2/bluedroid/BluetoothOppManager.h +++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.h @@ -11,20 +11,14 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" +#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" -class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; -namespace mozilla { -namespace dom { -class BlobParent; -} -} - BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/bluetooth2/bluez/BluetoothOppManager.cpp b/dom/bluetooth2/bluez/BluetoothOppManager.cpp index 26351aaea420..19bccd4423eb 100644 --- a/dom/bluetooth2/bluez/BluetoothOppManager.cpp +++ b/dom/bluetooth2/bluez/BluetoothOppManager.cpp @@ -14,7 +14,6 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth2/bluez/BluetoothOppManager.h b/dom/bluetooth2/bluez/BluetoothOppManager.h index d6e7bf6daa5c..d7de3917883a 100644 --- a/dom/bluetooth2/bluez/BluetoothOppManager.h +++ b/dom/bluetooth2/bluez/BluetoothOppManager.h @@ -11,20 +11,14 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" +#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" -class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; -namespace mozilla { -namespace dom { -class BlobParent; -} -} - BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/datastore/DataStoreDB.cpp b/dom/datastore/DataStoreDB.cpp index ee7c7b233348..56b9135c4ca7 100644 --- a/dom/datastore/DataStoreDB.cpp +++ b/dom/datastore/DataStoreDB.cpp @@ -7,22 +7,15 @@ #include "DataStoreDB.h" #include "DataStoreCallbacks.h" -#include "jsapi.h" #include "mozilla/dom/IDBDatabaseBinding.h" #include "mozilla/dom/IDBFactoryBinding.h" -#include "mozilla/dom/IDBObjectStoreBinding.h" #include "mozilla/dom/indexedDB/IDBDatabase.h" #include "mozilla/dom/indexedDB/IDBEvents.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/indexedDB/IDBIndex.h" #include "mozilla/dom/indexedDB/IDBObjectStore.h" #include "mozilla/dom/indexedDB/IDBRequest.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" -#include "nsComponentManagerUtils.h" -#include "nsContentUtils.h" #include "nsIDOMEvent.h" -#include "nsIPrincipal.h" -#include "nsIXPConnect.h" #define DATASTOREDB_VERSION 1 #define DATASTOREDB_NAME "DataStoreDB" @@ -70,9 +63,7 @@ public: MOZ_ASSERT(version.IsNull()); #endif - mDatabase->Close(); - - return NS_OK; + return mDatabase->Close(); } private: @@ -102,36 +93,7 @@ nsresult DataStoreDB::CreateFactoryIfNeeded() { if (!mFactory) { - nsresult rv; - nsCOMPtr principal = - do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - MOZ_ASSERT(xpc); - - AutoSafeJSContext cx; - - nsCOMPtr globalHolder; - rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - JS::Rooted global(cx, globalHolder->GetJSObject()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_UNEXPECTED; - } - - // The CreateSandbox call returns a proxy to the actual sandbox object. We - // don't need a proxy here. - global = js::UncheckedUnwrap(global); - - JSAutoCompartment ac(cx, global); - - rv = IDBFactory::CreateForDatastore(cx, global, getter_AddRefs(mFactory)); + nsresult rv = IDBFactory::Create(nullptr, getter_AddRefs(mFactory)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -339,7 +301,11 @@ DataStoreDB::Delete() mTransaction = nullptr; if (mDatabase) { - mDatabase->Close(); + rv = mDatabase->Close(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + mDatabase = nullptr; } diff --git a/dom/datastore/DataStoreRevision.cpp b/dom/datastore/DataStoreRevision.cpp index 1078df678b89..c12dc7ef0aeb 100644 --- a/dom/datastore/DataStoreRevision.cpp +++ b/dom/datastore/DataStoreRevision.cpp @@ -9,9 +9,8 @@ #include "DataStoreCallbacks.h" #include "DataStoreService.h" #include "mozilla/dom/DataStoreBinding.h" -#include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/ToJSValue.h" #include "nsIDOMEvent.h" namespace mozilla { diff --git a/dom/datastore/DataStoreService.cpp b/dom/datastore/DataStoreService.cpp index eef48ade2935..580246bc54be 100644 --- a/dom/datastore/DataStoreService.cpp +++ b/dom/datastore/DataStoreService.cpp @@ -22,8 +22,6 @@ #include "mozilla/dom/DOMError.h" #include "mozilla/dom/indexedDB/IDBCursor.h" #include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/unused.h" diff --git a/dom/datastore/tests/test_oop_events.html b/dom/datastore/tests/test_oop_events.html index 07132590485b..349c4ceee2f3 100644 --- a/dom/datastore/tests/test_oop_events.html +++ b/dom/datastore/tests/test_oop_events.html @@ -1,4 +1,4 @@ - + @@ -137,7 +137,7 @@ // Uninstall the apps function() { uninstallApp(gApps[0]); }, - function() { uninstallApp(gApps[1]); } + function() { uninstallApp(gApps[1]); }, ]; function runTest() { diff --git a/dom/devicestorage/DeviceStorageRequestChild.cpp b/dom/devicestorage/DeviceStorageRequestChild.cpp index 3b426a4c309b..5075f4ae8f7c 100644 --- a/dom/devicestorage/DeviceStorageRequestChild.cpp +++ b/dom/devicestorage/DeviceStorageRequestChild.cpp @@ -8,7 +8,7 @@ #include "DeviceStorageFileDescriptor.h" #include "nsDeviceStorage.h" #include "nsDOMFile.h" -#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/Blob.h" namespace mozilla { namespace dom { diff --git a/dom/devicestorage/DeviceStorageRequestParent.cpp b/dom/devicestorage/DeviceStorageRequestParent.cpp index 8ccc281d19b9..7ca5e776543c 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.cpp +++ b/dom/devicestorage/DeviceStorageRequestParent.cpp @@ -8,7 +8,7 @@ #include "nsIMIMEService.h" #include "nsCExternalHandlerService.h" #include "mozilla/unused.h" -#include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/ipc/Blob.h" #include "ContentParent.h" #include "nsProxyRelease.h" #include "AppProcessChecker.h" diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index aa0947a22f12..a3d7fe1bfd65 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -16,7 +16,7 @@ #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/FileSystemUtils.h" -#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/Blob.h" #include "mozilla/dom/PBrowserChild.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/Promise.h" diff --git a/dom/filehandle/FileStreamWrappers.cpp b/dom/filehandle/FileStreamWrappers.cpp index 688c315416ec..124da638ca90 100644 --- a/dom/filehandle/FileStreamWrappers.cpp +++ b/dom/filehandle/FileStreamWrappers.cpp @@ -8,9 +8,7 @@ #include "FileHelper.h" #include "MainThreadUtils.h" -#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -#include "mozilla/ipc/InputStreamParams.h" #include "MutableFile.h" #include "nsDebug.h" #include "nsError.h" @@ -18,10 +16,6 @@ #include "nsISeekableStream.h" #include "nsThreadUtils.h" -#ifdef DEBUG -#include "nsXULAppAPI.h" -#endif - namespace mozilla { namespace dom { @@ -133,8 +127,7 @@ FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream, NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper, FileStreamWrapper, - nsIInputStream, - nsIIPCSerializableInputStream) + nsIInputStream) NS_IMETHODIMP FileInputStreamWrapper::Close() @@ -237,26 +230,6 @@ FileInputStreamWrapper::IsNonBlocking(bool* _retval) return NS_OK; } -void -FileInputStreamWrapper::Serialize(InputStreamParams& aParams, - FileDescriptorArray& /* aFDs */) -{ - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr thisStream = do_QueryObject(this); - - aParams = mozilla::ipc::SameProcessInputStreamParams( - reinterpret_cast(thisStream.forget().take())); -} - -bool -FileInputStreamWrapper::Deserialize(const InputStreamParams& /* aParams */, - const FileDescriptorArray& /* aFDs */) -{ - MOZ_CRASH("Should never get here!"); -} - FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream, FileHelper* aFileHelper, uint64_t aOffset, diff --git a/dom/filehandle/FileStreamWrappers.h b/dom/filehandle/FileStreamWrappers.h index de847ec8c2f8..ff97c069cead 100644 --- a/dom/filehandle/FileStreamWrappers.h +++ b/dom/filehandle/FileStreamWrappers.h @@ -11,13 +11,8 @@ #include "nsCOMPtr.h" #include "nsIInputStream.h" #include "nsIOutputStream.h" -#include "nsIIPCSerializableInputStream.h" namespace mozilla { -namespace ipc { -class InputStreamParams; -} // namespace ipc - namespace dom { class FileHelper; @@ -51,15 +46,11 @@ protected: }; class FileInputStreamWrapper : public FileStreamWrapper, - public nsIInputStream, - public nsIIPCSerializableInputStream + public nsIInputStream { - typedef mozilla::ipc::InputStreamParams InputStreamParams; - public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIINPUTSTREAM - NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM FileInputStreamWrapper(nsISupports* aFileStream, FileHelper* aFileHelper, diff --git a/dom/filehandle/moz.build b/dom/filehandle/moz.build index ed76866e9b6f..a42e8a200dca 100644 --- a/dom/filehandle/moz.build +++ b/dom/filehandle/moz.build @@ -28,8 +28,6 @@ UNIFIED_SOURCES += [ FAIL_ON_WARNINGS = True -include('/ipc/chromium/chromium-config.mozbuild') - LOCAL_INCLUDES += [ '../base', ] diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index f6e1102f4086..518f024e4d60 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -13,8 +13,6 @@ #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "nsDOMFile.h" #include "nsIFile.h" #include "nsNetUtil.h" diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index d64b53b85de9..a70b6be3aebf 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -13,7 +13,6 @@ #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PContent.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/unused.h" #include "nsDOMFile.h" diff --git a/dom/filesystem/FileSystemTaskBase.h b/dom/filesystem/FileSystemTaskBase.h index d57231226399..50068bfe5d02 100644 --- a/dom/filesystem/FileSystemTaskBase.h +++ b/dom/filesystem/FileSystemTaskBase.h @@ -10,13 +10,13 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemRequestParent.h" #include "mozilla/dom/PFileSystemRequestChild.h" +#include "mozilla/dom/ipc/Blob.h" class nsIDOMFile; namespace mozilla { namespace dom { -class BlobParent; class FileSystemBase; class FileSystemParams; class Promise; diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 9e63d7834879..993a37913d25 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -11,8 +11,6 @@ #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "nsDOMFile.h" #include "nsIFile.h" #include "nsStringGlue.h" diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index 94d1207f84e4..fc0c3cf0e1ec 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -10,8 +10,6 @@ #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" #include "nsDOMFile.h" #include "nsIFile.h" #include "nsStringGlue.h" diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp deleted file mode 100644 index 39c72240c42e..000000000000 --- a/dom/indexedDB/ActorsChild.cpp +++ /dev/null @@ -1,2351 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ActorsChild.h" - -#include "BackgroundChildImpl.h" -#include "FileManager.h" -#include "IDBDatabase.h" -#include "IDBEvents.h" -#include "IDBFactory.h" -#include "IDBIndex.h" -#include "IDBObjectStore.h" -#include "IDBMutableFile.h" -#include "IDBRequest.h" -#include "IDBTransaction.h" -#include "IndexedDatabase.h" -#include "IndexedDatabaseInlines.h" -#include "mozilla/BasicEvents.h" -#include "mozilla/Maybe.h" -#include "mozilla/dom/PermissionMessageUtils.h" -#include "mozilla/dom/TabChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/ipc/BackgroundUtils.h" -#include "nsCOMPtr.h" -#include "nsContentUtils.h" -#include "nsIBFCacheEntry.h" -#include "nsIDocument.h" -#include "nsIDOMEvent.h" -#include "nsIEventTarget.h" -#include "nsPIDOMWindow.h" -#include "nsThreadUtils.h" -#include "nsTraceRefcnt.h" -#include "PermissionRequestBase.h" -#include "ProfilerHelpers.h" -#include "ReportInternalError.h" - -#ifdef DEBUG -#include "IndexedDatabaseManager.h" -#endif - -#define GC_ON_IPC_MESSAGES 0 - -#if defined(DEBUG) || GC_ON_IPC_MESSAGES - -#include "js/GCAPI.h" -#include "nsJSEnvironment.h" - -#define BUILD_GC_ON_IPC_MESSAGES - -#endif // DEBUG || GC_ON_IPC_MESSAGES - -namespace mozilla { -namespace dom { -namespace indexedDB { - -/******************************************************************************* - * Helpers - ******************************************************************************/ - -namespace { - -void -MaybeCollectGarbageOnIPCMessage() -{ -#ifdef BUILD_GC_ON_IPC_MESSAGES - static const bool kCollectGarbageOnIPCMessages = -#if GC_ON_IPC_MESSAGES - true; -#else - false; -#endif // GC_ON_IPC_MESSAGES - - if (!kCollectGarbageOnIPCMessages) { - return; - } - - static bool haveWarnedAboutGC = false; - static bool haveWarnedAboutNonMainThread = false; - - if (!haveWarnedAboutGC) { - haveWarnedAboutGC = true; - NS_WARNING("IndexedDB child actor GC debugging enabled!"); - } - - if (!NS_IsMainThread()) { - if (!haveWarnedAboutNonMainThread) { - haveWarnedAboutNonMainThread = true; - NS_WARNING("Don't know how to GC on a non-main thread yet."); - } - return; - } - - nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC); - nsJSContext::CycleCollectNow(); -#endif // BUILD_GC_ON_IPC_MESSAGES -} - -class MOZ_STACK_CLASS AutoSetCurrentTransaction MOZ_FINAL -{ - typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl; - - IDBTransaction* const mTransaction; - IDBTransaction* mPreviousTransaction; - IDBTransaction** mThreadLocalSlot; - -public: - explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction) - : mTransaction(aTransaction) - , mPreviousTransaction(nullptr) - , mThreadLocalSlot(nullptr) - { - if (aTransaction) { - BackgroundChildImpl::ThreadLocal* threadLocal = - BackgroundChildImpl::GetThreadLocalForCurrentThread(); - MOZ_ASSERT(threadLocal); - - // Hang onto this location for resetting later. - mThreadLocalSlot = &threadLocal->mCurrentTransaction; - - // Save the current value. - mPreviousTransaction = *mThreadLocalSlot; - - // Set the new value. - *mThreadLocalSlot = aTransaction; - } - } - - ~AutoSetCurrentTransaction() - { - MOZ_ASSERT_IF(mThreadLocalSlot, mTransaction); - - if (mThreadLocalSlot) { - MOZ_ASSERT(*mThreadLocalSlot == mTransaction); - - // Reset old value. - *mThreadLocalSlot = mPreviousTransaction; - } - } - - IDBTransaction* - Transaction() const - { - return mTransaction; - } -}; - -class MOZ_STACK_CLASS ResultHelper MOZ_FINAL - : public IDBRequest::ResultCallback -{ - IDBRequest* mRequest; - AutoSetCurrentTransaction mAutoTransaction; - - union - { - nsISupports* mISupports; - StructuredCloneReadInfo* mStructuredClone; - const nsTArray* mStructuredCloneArray; - const Key* mKey; - const nsTArray* mKeyArray; - const JS::Value* mJSVal; - const JS::Handle* mJSValHandle; - } mResult; - - enum - { - ResultTypeISupports, - ResultTypeStructuredClone, - ResultTypeStructuredCloneArray, - ResultTypeKey, - ResultTypeKeyArray, - ResultTypeJSVal, - ResultTypeJSValHandle, - } mResultType; - -public: - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - nsISupports* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeISupports) - { - MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!"); - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aResult); - - mResult.mISupports = aResult; - } - - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - StructuredCloneReadInfo* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeStructuredClone) - { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aResult); - - mResult.mStructuredClone = aResult; - } - - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - const nsTArray* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeStructuredCloneArray) - { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aResult); - - mResult.mStructuredCloneArray = aResult; - } - - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - const Key* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeKey) - { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aResult); - - mResult.mKey = aResult; - } - - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - const nsTArray* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeKeyArray) - { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aResult); - - mResult.mKeyArray = aResult; - } - - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - const JS::Value* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeJSVal) - { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(!aResult->isGCThing()); - - mResult.mJSVal = aResult; - } - - ResultHelper(IDBRequest* aRequest, - IDBTransaction* aTransaction, - const JS::Handle* aResult) - : mRequest(aRequest) - , mAutoTransaction(aTransaction) - , mResultType(ResultTypeJSValHandle) - { - MOZ_ASSERT(aRequest); - - mResult.mJSValHandle = aResult; - } - - IDBRequest* - Request() const - { - return mRequest; - } - - IDBTransaction* - Transaction() const - { - return mAutoTransaction.Transaction(); - } - - virtual nsresult - GetResult(JSContext* aCx, JS::MutableHandle aResult) MOZ_OVERRIDE - { - MOZ_ASSERT(aCx); - MOZ_ASSERT(mRequest); - - switch (mResultType) { - case ResultTypeISupports: - return GetResult(aCx, mResult.mISupports, aResult); - - case ResultTypeStructuredClone: - return GetResult(aCx, mResult.mStructuredClone, aResult); - - case ResultTypeStructuredCloneArray: - return GetResult(aCx, mResult.mStructuredCloneArray, aResult); - - case ResultTypeKey: - return GetResult(aCx, mResult.mKey, aResult); - - case ResultTypeKeyArray: - return GetResult(aCx, mResult.mKeyArray, aResult); - - case ResultTypeJSVal: - aResult.set(*mResult.mJSVal); - return NS_OK; - - case ResultTypeJSValHandle: - aResult.set(*mResult.mJSValHandle); - return NS_OK; - - default: - MOZ_CRASH("Unknown result type!"); - } - - MOZ_CRASH("Should never get here!"); - } - -private: - nsresult - GetResult(JSContext* aCx, - nsISupports* aSupports, - JS::MutableHandle aResult) - { - MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!"); - - if (!aSupports) { - aResult.setNull(); - return NS_OK; - } - - nsresult rv = nsContentUtils::WrapNative(aCx, aSupports, aResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return NS_OK; - } - - nsresult - GetResult(JSContext* aCx, - StructuredCloneReadInfo* aCloneInfo, - JS::MutableHandle aResult) - { - bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult); - - aCloneInfo->mCloneBuffer.clear(); - - if (NS_WARN_IF(!ok)) { - return NS_ERROR_DOM_DATA_CLONE_ERR; - } - - return NS_OK; - } - - nsresult - GetResult(JSContext* aCx, - const nsTArray* aCloneInfos, - JS::MutableHandle aResult) - { - JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); - if (NS_WARN_IF(!array)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (!aCloneInfos->IsEmpty()) { - const uint32_t count = aCloneInfos->Length(); - - if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t index = 0; index < count; index++) { - auto& cloneInfo = - const_cast(aCloneInfos->ElementAt(index)); - - JS::Rooted value(aCx); - - nsresult rv = GetResult(aCx, &cloneInfo, &value); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - } - - aResult.setObject(*array); - return NS_OK; - } - - nsresult - GetResult(JSContext* aCx, - const Key* aKey, - JS::MutableHandle aResult) - { - nsresult rv = aKey->ToJSVal(aCx, aResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; - } - - nsresult - GetResult(JSContext* aCx, - const nsTArray* aKeys, - JS::MutableHandle aResult) - { - JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); - if (NS_WARN_IF(!array)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (!aKeys->IsEmpty()) { - const uint32_t count = aKeys->Length(); - - if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t index = 0; index < count; index++) { - const Key& key = aKeys->ElementAt(index); - MOZ_ASSERT(!key.IsUnset()); - - JS::Rooted value(aCx); - - nsresult rv = GetResult(aCx, &key, &value); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - } - - aResult.setObject(*array); - return NS_OK; - } -}; - -class PermissionRequestMainProcessHelper MOZ_FINAL - : public PermissionRequestBase -{ - BackgroundFactoryRequestChild* mActor; - nsRefPtr mFactory; - -public: - PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor, - IDBFactory* aFactory, - nsPIDOMWindow* aWindow, - nsIPrincipal* aPrincipal) - : PermissionRequestBase(aWindow, aPrincipal) - , mActor(aActor) - , mFactory(aFactory) - { - MOZ_ASSERT(aActor); - MOZ_ASSERT(aFactory); - aActor->AssertIsOnOwningThread(); - } - -protected: - ~PermissionRequestMainProcessHelper() - { } - -private: - virtual void - OnPromptComplete(PermissionValue aPermissionValue) MOZ_OVERRIDE; -}; - -class PermissionRequestChildProcessActor MOZ_FINAL - : public PIndexedDBPermissionRequestChild -{ - BackgroundFactoryRequestChild* mActor; - nsRefPtr mFactory; - -public: - PermissionRequestChildProcessActor(BackgroundFactoryRequestChild* aActor, - IDBFactory* aFactory) - : mActor(aActor) - , mFactory(aFactory) - { - MOZ_ASSERT(aActor); - MOZ_ASSERT(aFactory); - aActor->AssertIsOnOwningThread(); - } - -protected: - ~PermissionRequestChildProcessActor() - { } - - virtual bool - Recv__delete__(const uint32_t& aPermission) MOZ_OVERRIDE; -}; - -void -ConvertActorsToBlobs(IDBDatabase* aDatabase, - const SerializedStructuredCloneReadInfo& aCloneReadInfo, - nsTArray& aFiles) -{ - MOZ_ASSERT(aFiles.IsEmpty()); - - const nsTArray& blobs = aCloneReadInfo.blobsChild(); - const nsTArray& fileInfos = aCloneReadInfo.fileInfos(); - - MOZ_ASSERT_IF(IndexedDatabaseManager::IsMainProcess(), - blobs.Length() == fileInfos.Length()); - MOZ_ASSERT_IF(!IndexedDatabaseManager::IsMainProcess(), fileInfos.IsEmpty()); - - if (!blobs.IsEmpty()) { - const uint32_t count = blobs.Length(); - aFiles.SetCapacity(count); - - for (uint32_t index = 0; index < count; index++) { - BlobChild* actor = static_cast(blobs[index]); - - nsCOMPtr blob = actor->GetBlob(); - MOZ_ASSERT(blob); - - nsRefPtr fileInfo; - if (!fileInfos.IsEmpty()) { - fileInfo = dont_AddRef(reinterpret_cast(fileInfos[index])); - - MOZ_ASSERT(fileInfo); - MOZ_ASSERT(fileInfo->Id() > 0); - - blob->AddFileInfo(fileInfo); - } - - aDatabase->NoteReceivedBlob(blob); - - StructuredCloneFile* file = aFiles.AppendElement(); - MOZ_ASSERT(file); - - file->mFile.swap(blob); - file->mFileInfo.swap(fileInfo); - } - } -} - -void -DispatchSuccessEvent(ResultHelper* aResultHelper, - nsIDOMEvent* aEvent = nullptr) -{ - MOZ_ASSERT(aResultHelper); - - PROFILER_LABEL("IndexedDB", - "DispatchSuccessEvent", - js::ProfileEntry::Category::STORAGE); - - nsRefPtr request = aResultHelper->Request(); - MOZ_ASSERT(request); - request->AssertIsOnOwningThread(); - - nsRefPtr transaction = aResultHelper->Transaction(); - - nsCOMPtr successEvent; - if (!aEvent) { - successEvent = CreateGenericEvent(request, - nsDependentString(kSuccessEventType), - eDoesNotBubble, - eNotCancelable); - if (NS_WARN_IF(!successEvent)) { - return; - } - - aEvent = successEvent; - } - - request->SetResultCallback(aResultHelper); - - MOZ_ASSERT(aEvent); - MOZ_ASSERT_IF(transaction, transaction->IsOpen()); - - bool dummy; - nsresult rv = request->DispatchEvent(aEvent, &dummy); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - MOZ_ASSERT_IF(transaction, - transaction->IsOpen() || transaction->IsAborted()); - - WidgetEvent* internalEvent = aEvent->GetInternalNSEvent(); - MOZ_ASSERT(internalEvent); - - if (transaction && - transaction->IsOpen() && - internalEvent->mFlags.mExceptionHasBeenRisen) { - transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); - } -} - -void -DispatchErrorEvent(IDBRequest* aRequest, - nsresult aErrorCode, - IDBTransaction* aTransaction = nullptr, - nsIDOMEvent* aEvent = nullptr) -{ - MOZ_ASSERT(aRequest); - aRequest->AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aErrorCode)); - MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); - - PROFILER_LABEL("IndexedDB", - "DispatchErrorEvent", - js::ProfileEntry::Category::STORAGE); - - nsRefPtr request = aRequest; - nsRefPtr transaction = aTransaction; - - request->SetError(aErrorCode); - - nsCOMPtr errorEvent; - if (!aEvent) { - // Make an error event and fire it at the target. - errorEvent = CreateGenericEvent(request, - nsDependentString(kErrorEventType), - eDoesBubble, - eCancelable); - if (NS_WARN_IF(!errorEvent)) { - return; - } - - aEvent = errorEvent; - } - - Maybe asct; - if (aTransaction) { - asct.emplace(aTransaction); - } - - bool doDefault; - nsresult rv = request->DispatchEvent(aEvent, &doDefault); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted()); - - if (transaction && transaction->IsOpen()) { - WidgetEvent* internalEvent = aEvent->GetInternalNSEvent(); - MOZ_ASSERT(internalEvent); - - if (internalEvent->mFlags.mExceptionHasBeenRisen) { - transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); - } else if (doDefault) { - transaction->Abort(request); - } - } -} - -} // anonymous namespace - -/******************************************************************************* - * Local class implementations - ******************************************************************************/ - -void -PermissionRequestMainProcessHelper::OnPromptComplete( - PermissionValue aPermissionValue) -{ - MOZ_ASSERT(mActor); - mActor->AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - mActor->SendPermissionRetry(); - - mActor = nullptr; - mFactory = nullptr; -} - -bool -PermissionRequestChildProcessActor::Recv__delete__( - const uint32_t& /* aPermission */) -{ - MOZ_ASSERT(mActor); - mActor->AssertIsOnOwningThread(); - MOZ_ASSERT(mFactory); - - MaybeCollectGarbageOnIPCMessage(); - - nsRefPtr factory; - mFactory.swap(factory); - - mActor->SendPermissionRetry(); - mActor = nullptr; - - return true; -} - -/******************************************************************************* - * BackgroundRequestChildBase - ******************************************************************************/ - -BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest) - : mRequest(aRequest) - , mActorDestroyed(false) -{ - MOZ_ASSERT(aRequest); - aRequest->AssertIsOnOwningThread(); - - MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase); -} - -BackgroundRequestChildBase::~BackgroundRequestChildBase() -{ - AssertIsOnOwningThread(); - - MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase); -} - -#ifdef DEBUG - -void -BackgroundRequestChildBase::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mRequest); - mRequest->AssertIsOnOwningThread(); -} - -#endif // DEBUG - -void -BackgroundRequestChildBase::NoteActorDestroyed() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; -} - -/******************************************************************************* - * BackgroundFactoryChild - ******************************************************************************/ - -BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory) - : mFactory(aFactory) -#ifdef DEBUG - , mOwningThread(NS_GetCurrentThread()) -#endif -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aFactory); - aFactory->AssertIsOnOwningThread(); - - MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild); -} - -BackgroundFactoryChild::~BackgroundFactoryChild() -{ - MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild); -} - -#ifdef DEBUG - -void -BackgroundFactoryChild::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mOwningThread); - - bool current; - MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); - MOZ_ASSERT(current); -} - -#endif // DEBUG - -void -BackgroundFactoryChild::SendDeleteMeInternal() -{ - AssertIsOnOwningThread(); - - if (mFactory) { - mFactory->ClearBackgroundActor(); - mFactory = nullptr; - - MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe()); - } -} - -void -BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - if (mFactory) { - mFactory->ClearBackgroundActor(); -#ifdef DEBUG - mFactory = nullptr; -#endif - } -} - -PBackgroundIDBFactoryRequestChild* -BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild( - const FactoryRequestParams& aParams) -{ - MOZ_CRASH("PBackgroundIDBFactoryRequestChild actors should be manually " - "constructed!"); -} - -bool -BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild( - PBackgroundIDBFactoryRequestChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -PBackgroundIDBDatabaseChild* -BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild( - const DatabaseSpec& aSpec, - PBackgroundIDBFactoryRequestChild* aRequest) -{ - AssertIsOnOwningThread(); - - auto request = static_cast(aRequest); - MOZ_ASSERT(request); - - return new BackgroundDatabaseChild(aSpec, request); -} - -bool -BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild( - PBackgroundIDBDatabaseChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -/******************************************************************************* - * BackgroundFactoryRequestChild - ******************************************************************************/ - -BackgroundFactoryRequestChild::BackgroundFactoryRequestChild( - IDBFactory* aFactory, - IDBOpenDBRequest* aOpenRequest, - bool aIsDeleteOp, - uint64_t aRequestedVersion, - PersistenceType aPersistenceType) - : BackgroundRequestChildBase(aOpenRequest) - , mFactory(aFactory) - , mRequestedVersion(aRequestedVersion) - , mPersistenceType(aPersistenceType) - , mIsDeleteOp(aIsDeleteOp) -{ - // Can't assert owning thread here because IPDL has not yet set our manager! - MOZ_ASSERT(aFactory); - aFactory->AssertIsOnOwningThread(); - MOZ_ASSERT(aOpenRequest); - - MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild); -} - -BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() -{ - MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild); -} - -IDBOpenDBRequest* -BackgroundFactoryRequestChild::GetOpenDBRequest() const -{ - AssertIsOnOwningThread(); - - IDBRequest* baseRequest = BackgroundRequestChildBase::GetDOMObject(); - return static_cast(baseRequest); -} - -bool -BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aResponse)); - MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); - - mRequest->Reset(); - - DispatchErrorEvent(mRequest, aResponse); - - return true; -} - -bool -BackgroundFactoryRequestChild::HandleResponse( - const OpenDatabaseRequestResponse& aResponse) -{ - AssertIsOnOwningThread(); - - mRequest->Reset(); - - auto databaseActor = - static_cast(aResponse.databaseChild()); - MOZ_ASSERT(databaseActor); - - databaseActor->EnsureDOMObject(); - - IDBDatabase* database = databaseActor->GetDOMObject(); - MOZ_ASSERT(database); - - ResultHelper helper(mRequest, nullptr, - static_cast(database)); - - DispatchSuccessEvent(&helper); - - databaseActor->ReleaseDOMObject(); - - return true; -} - -bool -BackgroundFactoryRequestChild::HandleResponse( - const DeleteDatabaseRequestResponse& aResponse) -{ - AssertIsOnOwningThread(); - - ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue); - - nsCOMPtr successEvent = - IDBVersionChangeEvent::Create(mRequest, - nsDependentString(kSuccessEventType), - aResponse.previousVersion()); - if (NS_WARN_IF(!successEvent)) { - return false; - } - - DispatchSuccessEvent(&helper, successEvent); - - return true; -} - -void -BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - NoteActorDestroyed(); -} - -bool -BackgroundFactoryRequestChild::Recv__delete__( - const FactoryRequestResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - - MaybeCollectGarbageOnIPCMessage(); - - switch (aResponse.type()) { - case FactoryRequestResponse::Tnsresult: - return HandleResponse(aResponse.get_nsresult()); - - case FactoryRequestResponse::TOpenDatabaseRequestResponse: - return HandleResponse(aResponse.get_OpenDatabaseRequestResponse()); - - case FactoryRequestResponse::TDeleteDatabaseRequestResponse: - return HandleResponse(aResponse.get_DeleteDatabaseRequestResponse()); - - default: - MOZ_CRASH("Unknown response type!"); - } - - MOZ_CRASH("Should never get here!"); -} - -bool -BackgroundFactoryRequestChild::RecvPermissionChallenge( - const PrincipalInfo& aPrincipalInfo) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - if (!NS_IsMainThread()) { - MOZ_CRASH("Implement me for workers!"); - } - - nsresult rv; - nsCOMPtr principal = - mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - if (XRE_GetProcessType() == GeckoProcessType_Default) { - nsCOMPtr window = mFactory->GetParentObject(); - MOZ_ASSERT(window); - - nsRefPtr helper = - new PermissionRequestMainProcessHelper(this, mFactory, window, principal); - - PermissionRequestBase::PermissionValue permission; - if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) { - return false; - } - - MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || - permission == PermissionRequestBase::kPermissionDenied || - permission == PermissionRequestBase::kPermissionPrompt); - - if (permission != PermissionRequestBase::kPermissionPrompt) { - SendPermissionRetry(); - } - return true; - } - - nsRefPtr tabChild = mFactory->GetTabChild(); - MOZ_ASSERT(tabChild); - - IPC::Principal ipcPrincipal(principal); - - auto* actor = new PermissionRequestChildProcessActor(this, mFactory); - - tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal); - - return true; -} - -bool -BackgroundFactoryRequestChild::RecvBlocked(const uint64_t& aCurrentVersion) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - - MaybeCollectGarbageOnIPCMessage(); - - const nsDependentString type(kBlockedEventType); - - nsCOMPtr blockedEvent; - if (mIsDeleteOp) { - blockedEvent = - IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion); - } else { - blockedEvent = - IDBVersionChangeEvent::Create(mRequest, - type, - aCurrentVersion, - mRequestedVersion); - } - - if (NS_WARN_IF(!blockedEvent)) { - return false; - } - - nsRefPtr kungFuDeathGrip = mRequest; - - bool dummy; - NS_WARN_IF(NS_FAILED(mRequest->DispatchEvent(blockedEvent, &dummy))); - - return true; -} - -/******************************************************************************* - * BackgroundDatabaseChild - ******************************************************************************/ - -BackgroundDatabaseChild::BackgroundDatabaseChild( - const DatabaseSpec& aSpec, - BackgroundFactoryRequestChild* aOpenRequestActor) - : mSpec(new DatabaseSpec(aSpec)) - , mOpenRequestActor(aOpenRequestActor) - , mDatabase(nullptr) - , mPersistenceType(aOpenRequestActor->mPersistenceType) -{ - // Can't assert owning thread here because IPDL has not yet set our manager! - MOZ_ASSERT(aOpenRequestActor); - - MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild); -} - -BackgroundDatabaseChild::~BackgroundDatabaseChild() -{ - MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild); -} - -void -BackgroundDatabaseChild::SendDeleteMeInternal() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mTemporaryStrongDatabase); - MOZ_ASSERT(!mOpenRequestActor); - - if (mDatabase) { - mDatabase->ClearBackgroundActor(); - mDatabase = nullptr; - - MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe()); - } -} - -void -BackgroundDatabaseChild::EnsureDOMObject() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mOpenRequestActor); - - if (mTemporaryStrongDatabase) { - MOZ_ASSERT(!mSpec); - return; - } - - MOZ_ASSERT(mSpec); - - auto request = mOpenRequestActor->GetDOMObject(); - MOZ_ASSERT(request); - - auto factory = - static_cast(Manager())->GetDOMObject(); - MOZ_ASSERT(factory); - - mTemporaryStrongDatabase = - IDBDatabase::Create(request, factory, this, mSpec); - - MOZ_ASSERT(mTemporaryStrongDatabase); - mTemporaryStrongDatabase->AssertIsOnOwningThread(); - - mDatabase = mTemporaryStrongDatabase; - mSpec.forget(); -} - -void -BackgroundDatabaseChild::ReleaseDOMObject() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mTemporaryStrongDatabase); - mTemporaryStrongDatabase->AssertIsOnOwningThread(); - MOZ_ASSERT(mOpenRequestActor); - MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); - - mOpenRequestActor = nullptr; - - // This may be the final reference to the IDBDatabase object so we may end up - // calling SendDeleteMeInternal() here. Make sure everything is cleaned up - // properly before proceeding. - mTemporaryStrongDatabase = nullptr; -} - -void -BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - if (mDatabase) { - mDatabase->ClearBackgroundActor(); -#ifdef DEBUG - mDatabase = nullptr; -#endif - } -} - -PBackgroundIDBDatabaseFileChild* -BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild( - const BlobOrInputStream& aBlobOrInputStream) -{ - MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!"); -} - -bool -BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild( - PBackgroundIDBDatabaseFileChild* aActor) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aActor); - - delete aActor; - return true; -} - -PBackgroundIDBTransactionChild* -BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild( - const nsTArray& aObjectStoreNames, - const Mode& aMode) -{ - MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually " - "constructed!"); -} - -bool -BackgroundDatabaseChild::DeallocPBackgroundIDBTransactionChild( - PBackgroundIDBTransactionChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -PBackgroundIDBVersionChangeTransactionChild* -BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild( - const uint64_t& aCurrentVersion, - const uint64_t& aRequestedVersion, - const int64_t& aNextObjectStoreId, - const int64_t& aNextIndexId) -{ - AssertIsOnOwningThread(); - - IDBOpenDBRequest* request = mOpenRequestActor->GetOpenDBRequest(); - MOZ_ASSERT(request); - - return new BackgroundVersionChangeTransactionChild(request); -} - -bool -BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor( - PBackgroundIDBVersionChangeTransactionChild* aActor, - const uint64_t& aCurrentVersion, - const uint64_t& aRequestedVersion, - const int64_t& aNextObjectStoreId, - const int64_t& aNextIndexId) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(mOpenRequestActor); - - MaybeCollectGarbageOnIPCMessage(); - - EnsureDOMObject(); - - auto actor = static_cast(aActor); - - nsRefPtr transaction = - IDBTransaction::CreateVersionChange(mDatabase, actor, aNextObjectStoreId, - aNextIndexId); - if (NS_WARN_IF(!transaction)) { - return false; - } - - transaction->AssertIsOnOwningThread(); - - actor->SetDOMTransaction(transaction); - - mDatabase->EnterSetVersionTransaction(aRequestedVersion); - - nsRefPtr request = mOpenRequestActor->GetOpenDBRequest(); - MOZ_ASSERT(request); - - request->SetTransaction(transaction); - - nsCOMPtr upgradeNeededEvent = - IDBVersionChangeEvent::Create(request, - nsDependentString(kUpgradeNeededEventType), - aCurrentVersion, - aRequestedVersion); - if (NS_WARN_IF(!upgradeNeededEvent)) { - return false; - } - - ResultHelper helper(request, transaction, - static_cast(mDatabase)); - - DispatchSuccessEvent(&helper, upgradeNeededEvent); - - return true; -} - -bool -BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild( - PBackgroundIDBVersionChangeTransactionChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -bool -BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, - const NullableVersion& aNewVersion) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - if (!mDatabase || mDatabase->IsClosed()) { - return true; - } - - nsRefPtr kungFuDeathGrip = mDatabase; - - // Handle bfcache'd windows. - if (nsPIDOMWindow* owner = mDatabase->GetOwner()) { - // The database must be closed if the window is already frozen. - bool shouldAbortAndClose = owner->IsFrozen(); - - // Anything in the bfcache has to be evicted and then we have to close the - // database also. - if (nsCOMPtr doc = owner->GetExtantDoc()) { - if (nsCOMPtr bfCacheEntry = doc->GetBFCacheEntry()) { - bfCacheEntry->RemoveFromBFCacheSync(); - shouldAbortAndClose = true; - } - } - - if (shouldAbortAndClose) { - // Invalidate() doesn't close the database in the parent, so we have - // to call Close() and AbortTransactions() manually. - mDatabase->AbortTransactions(); - mDatabase->Close(); - return true; - } - } - - // Otherwise fire a versionchange event. - const nsDependentString type(kVersionChangeEventType); - - nsCOMPtr versionChangeEvent; - - switch (aNewVersion.type()) { - case NullableVersion::Tnull_t: - versionChangeEvent = - IDBVersionChangeEvent::Create(mDatabase, type, aOldVersion); - break; - - case NullableVersion::Tuint64_t: - versionChangeEvent = - IDBVersionChangeEvent::Create(mDatabase, - type, - aOldVersion, - aNewVersion.get_uint64_t()); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - if (NS_WARN_IF(!versionChangeEvent)) { - return false; - } - - bool dummy; - NS_WARN_IF(NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))); - - if (!mDatabase->IsClosed()) { - SendBlocked(); - } - - return true; -} - -bool -BackgroundDatabaseChild::RecvInvalidate() -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - if (mDatabase) { - mDatabase->Invalidate(); - } - - return true; -} - -/******************************************************************************* - * BackgroundTransactionBase - ******************************************************************************/ - -BackgroundTransactionBase::BackgroundTransactionBase() -: mTransaction(nullptr) -{ - MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); -} - -BackgroundTransactionBase::BackgroundTransactionBase( - IDBTransaction* aTransaction) - : mTemporaryStrongTransaction(aTransaction) - , mTransaction(aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - - MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); -} - -BackgroundTransactionBase::~BackgroundTransactionBase() -{ - MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionBase); -} - -#ifdef DEBUG - -void -BackgroundTransactionBase::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnOwningThread(); -} - -#endif // DEBUG - -void -BackgroundTransactionBase::NoteActorDestroyed() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction); - - if (mTransaction) { - mTransaction->ClearBackgroundActor(); - - // Normally this would be DEBUG-only but NoteActorDestroyed is also called - // from SendDeleteMeInternal. In that case we're going to receive an actual - // ActorDestroy call later and we don't want to touch a dead object. - mTemporaryStrongTransaction = nullptr; - mTransaction = nullptr; - } -} - -void -BackgroundTransactionBase::SetDOMTransaction(IDBTransaction* aTransaction) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - MOZ_ASSERT(!mTemporaryStrongTransaction); - MOZ_ASSERT(!mTransaction); - - mTemporaryStrongTransaction = aTransaction; - mTransaction = aTransaction; -} - -void -BackgroundTransactionBase::NoteComplete() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction); - - mTemporaryStrongTransaction = nullptr; -} - -/******************************************************************************* - * BackgroundTransactionChild - ******************************************************************************/ - -BackgroundTransactionChild::BackgroundTransactionChild( - IDBTransaction* aTransaction) - : BackgroundTransactionBase(aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - - MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild); -} - -BackgroundTransactionChild::~BackgroundTransactionChild() -{ - MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild); -} - -#ifdef DEBUG - -void -BackgroundTransactionChild::AssertIsOnOwningThread() const -{ - static_cast(Manager())->AssertIsOnOwningThread(); -} - -#endif // DEBUG - -void -BackgroundTransactionChild::SendDeleteMeInternal() -{ - AssertIsOnOwningThread(); - - if (mTransaction) { - NoteActorDestroyed(); - - MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe()); - } -} - -void -BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - NoteActorDestroyed(); -} - -bool -BackgroundTransactionChild::RecvComplete(const nsresult& aResult) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mTransaction); - - MaybeCollectGarbageOnIPCMessage(); - - mTransaction->FireCompleteOrAbortEvents(aResult); - - NoteComplete(); - return true; -} - -PBackgroundIDBRequestChild* -BackgroundTransactionChild::AllocPBackgroundIDBRequestChild( - const RequestParams& aParams) -{ - MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually " - "constructed!"); -} - -bool -BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild( - PBackgroundIDBRequestChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -PBackgroundIDBCursorChild* -BackgroundTransactionChild::AllocPBackgroundIDBCursorChild( - const OpenCursorParams& aParams) -{ - AssertIsOnOwningThread(); - - MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); -} - -bool -BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild( - PBackgroundIDBCursorChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -/******************************************************************************* - * BackgroundVersionChangeTransactionChild - ******************************************************************************/ - -BackgroundVersionChangeTransactionChild:: -BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest) - : mOpenDBRequest(aOpenDBRequest) -{ - MOZ_ASSERT(aOpenDBRequest); - aOpenDBRequest->AssertIsOnOwningThread(); - - MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild); -} - -BackgroundVersionChangeTransactionChild:: -~BackgroundVersionChangeTransactionChild() -{ - AssertIsOnOwningThread(); - - MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild); -} - -#ifdef DEBUG - -void -BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const -{ - static_cast(Manager())->AssertIsOnOwningThread(); -} - -#endif // DEBUG - -void -BackgroundVersionChangeTransactionChild::SendDeleteMeInternal() -{ - AssertIsOnOwningThread(); - - if (mTransaction) { - NoteActorDestroyed(); - - MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild:: - SendDeleteMe()); - } -} - -void -BackgroundVersionChangeTransactionChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - mOpenDBRequest = nullptr; - - NoteActorDestroyed(); -} - -bool -BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - if (!mTransaction) { - return true; - } - - MOZ_ASSERT(mOpenDBRequest); - - IDBDatabase* database = mTransaction->Database(); - MOZ_ASSERT(database); - - database->ExitSetVersionTransaction(); - - if (NS_FAILED(aResult)) { - database->Close(); - } - - mTransaction->FireCompleteOrAbortEvents(aResult); - - mOpenDBRequest->SetTransaction(nullptr); - mOpenDBRequest = nullptr; - - NoteComplete(); - return true; -} - -PBackgroundIDBRequestChild* -BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild( - const RequestParams& aParams) -{ - MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually " - "constructed!"); -} - -bool -BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild( - PBackgroundIDBRequestChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -PBackgroundIDBCursorChild* -BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild( - const OpenCursorParams& aParams) -{ - AssertIsOnOwningThread(); - - MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); -} - -bool -BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild( - PBackgroundIDBCursorChild* aActor) -{ - MOZ_ASSERT(aActor); - - delete static_cast(aActor); - return true; -} - -/******************************************************************************* - * BackgroundRequestChild - ******************************************************************************/ - -BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest) - : BackgroundRequestChildBase(aRequest) - , mTransaction(aRequest->GetTransaction()) -{ - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnOwningThread(); - - MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild); - - mTransaction->OnNewRequest(); -} - -BackgroundRequestChild::~BackgroundRequestChild() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(!IsActorDestroyed(), mTransaction); - - MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild); - - MaybeFinishTransactionEarly(); -} - -void -BackgroundRequestChild::HoldFileInfosUntilComplete( - nsTArray>& aFileInfos) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mFileInfos.IsEmpty()); - - mFileInfos.SwapElements(aFileInfos); -} - -void -BackgroundRequestChild::MaybeFinishTransactionEarly() -{ - AssertIsOnOwningThread(); - - if (mTransaction) { - mTransaction->AssertIsOnOwningThread(); - - mTransaction->OnRequestFinished(); - mTransaction = nullptr; - } -} - -bool -BackgroundRequestChild::HandleResponse(nsresult aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aResponse)); - MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); - MOZ_ASSERT(mTransaction); - - DispatchErrorEvent(mRequest, aResponse, mTransaction); - return true; -} - -bool -BackgroundRequestChild::HandleResponse(const Key& aResponse) -{ - AssertIsOnOwningThread(); - - ResultHelper helper(mRequest, mTransaction, &aResponse); - - DispatchSuccessEvent(&helper); - return true; -} - -bool -BackgroundRequestChild::HandleResponse(const nsTArray& aResponse) -{ - AssertIsOnOwningThread(); - - ResultHelper helper(mRequest, mTransaction, &aResponse); - - DispatchSuccessEvent(&helper); - return true; -} - -bool -BackgroundRequestChild::HandleResponse( - const SerializedStructuredCloneReadInfo& aResponse) -{ - AssertIsOnOwningThread(); - - // XXX Fix this somehow... - auto& serializedCloneInfo = - const_cast(aResponse); - - StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo)); - cloneReadInfo.mDatabase = mTransaction->Database(); - - ConvertActorsToBlobs(mTransaction->Database(), - aResponse, - cloneReadInfo.mFiles); - - ResultHelper helper(mRequest, mTransaction, &cloneReadInfo); - - DispatchSuccessEvent(&helper); - return true; -} - -bool -BackgroundRequestChild::HandleResponse( - const nsTArray& aResponse) -{ - AssertIsOnOwningThread(); - - nsTArray cloneReadInfos; - - if (!aResponse.IsEmpty()) { - const uint32_t count = aResponse.Length(); - - cloneReadInfos.SetCapacity(count); - - IDBDatabase* database = mTransaction->Database(); - - for (uint32_t index = 0; index < count; index++) { - // XXX Fix this somehow... - auto& serializedCloneInfo = - const_cast(aResponse[index]); - - StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement(); - - *cloneReadInfo = Move(serializedCloneInfo); - - cloneReadInfo->mDatabase = mTransaction->Database(); - - ConvertActorsToBlobs(database, - serializedCloneInfo, - cloneReadInfo->mFiles); - } - } - - ResultHelper helper(mRequest, mTransaction, &cloneReadInfos); - - DispatchSuccessEvent(&helper); - return true; -} - -bool -BackgroundRequestChild::HandleResponse(JS::Handle aResponse) -{ - AssertIsOnOwningThread(); - - ResultHelper helper(mRequest, mTransaction, &aResponse); - - DispatchSuccessEvent(&helper); - return true; -} - -bool -BackgroundRequestChild::HandleResponse(uint64_t aResponse) -{ - AssertIsOnOwningThread(); - - JS::Value response(JS::NumberValue(aResponse)); - - ResultHelper helper(mRequest, mTransaction, &response); - - DispatchSuccessEvent(&helper); - return true; -} - -void -BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - MaybeCollectGarbageOnIPCMessage(); - - MaybeFinishTransactionEarly(); - - NoteActorDestroyed(); -} - -bool -BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - - MaybeCollectGarbageOnIPCMessage(); - - // Always fire an "error" event with ABORT_ERR if the transaction was aborted, - // even if the request succeeded or failed with another error. - if (mTransaction->IsAborted()) { - return HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); - } - - switch (aResponse.type()) { - case RequestResponse::Tnsresult: - return HandleResponse(aResponse.get_nsresult()); - - case RequestResponse::TObjectStoreAddResponse: - return HandleResponse(aResponse.get_ObjectStoreAddResponse().key()); - - case RequestResponse::TObjectStorePutResponse: - return HandleResponse(aResponse.get_ObjectStorePutResponse().key()); - - case RequestResponse::TObjectStoreGetResponse: - return HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo()); - - case RequestResponse::TObjectStoreGetAllResponse: - return HandleResponse(aResponse.get_ObjectStoreGetAllResponse() - .cloneInfos()); - - case RequestResponse::TObjectStoreGetAllKeysResponse: - return HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse() - .keys()); - - case RequestResponse::TObjectStoreDeleteResponse: - return HandleResponse(JS::UndefinedHandleValue); - - case RequestResponse::TObjectStoreClearResponse: - return HandleResponse(JS::UndefinedHandleValue); - - case RequestResponse::TObjectStoreCountResponse: - return HandleResponse(aResponse.get_ObjectStoreCountResponse().count()); - - case RequestResponse::TIndexGetResponse: - return HandleResponse(aResponse.get_IndexGetResponse().cloneInfo()); - - case RequestResponse::TIndexGetKeyResponse: - return HandleResponse(aResponse.get_IndexGetKeyResponse().key()); - - case RequestResponse::TIndexGetAllResponse: - return HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos()); - - case RequestResponse::TIndexGetAllKeysResponse: - return HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys()); - - case RequestResponse::TIndexCountResponse: - return HandleResponse(aResponse.get_IndexCountResponse().count()); - - default: - MOZ_CRASH("Unknown response type!"); - } - - MOZ_CRASH("Should never get here!"); -} - -/******************************************************************************* - * BackgroundCursorChild - ******************************************************************************/ - -class BackgroundCursorChild::DelayedDeleteRunnable MOZ_FINAL - : public nsIRunnable -{ - BackgroundCursorChild* mActor; - nsRefPtr mRequest; - -public: - explicit DelayedDeleteRunnable(BackgroundCursorChild* aActor) - : mActor(aActor) - , mRequest(aActor->mRequest) - { - MOZ_ASSERT(aActor); - aActor->AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - } - - // Does not need to be threadsafe since this only runs on one thread. - NS_DECL_ISUPPORTS - -private: - ~DelayedDeleteRunnable() - { } - - NS_DECL_NSIRUNNABLE -}; - -BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - Direction aDirection) - : mRequest(aRequest) - , mTransaction(aRequest->GetTransaction()) - , mObjectStore(aObjectStore) - , mIndex(nullptr) - , mCursor(nullptr) - , mStrongRequest(aRequest) - , mDirection(aDirection) -{ - MOZ_ASSERT(aObjectStore); - aObjectStore->AssertIsOnOwningThread(); - MOZ_ASSERT(mTransaction); - - MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild); - -#ifdef DEBUG - mOwningThread = PR_GetCurrentThread(); - MOZ_ASSERT(mOwningThread); -#endif -} - -BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest, - IDBIndex* aIndex, - Direction aDirection) - : mRequest(aRequest) - , mTransaction(aRequest->GetTransaction()) - , mObjectStore(nullptr) - , mIndex(aIndex) - , mCursor(nullptr) - , mStrongRequest(aRequest) - , mDirection(aDirection) -{ - MOZ_ASSERT(aIndex); - aIndex->AssertIsOnOwningThread(); - MOZ_ASSERT(mTransaction); - - MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild); - -#ifdef DEBUG - mOwningThread = PR_GetCurrentThread(); - MOZ_ASSERT(mOwningThread); -#endif -} - -BackgroundCursorChild::~BackgroundCursorChild() -{ - MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild); -} - -#ifdef DEBUG - -void -BackgroundCursorChild::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mOwningThread == PR_GetCurrentThread()); -} - -#endif // DEBUG - -void -BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - // Make sure all our DOM objects stay alive. - mStrongRequest = mRequest; - mStrongCursor = mCursor; - - MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done); - mRequest->Reset(); - - mTransaction->OnNewRequest(); - - MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams)); -} - -void -BackgroundCursorChild::SendDeleteMeInternal() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - mRequest = nullptr; - mTransaction = nullptr; - mObjectStore = nullptr; - mIndex = nullptr; - - if (mCursor) { - mCursor->ClearBackgroundActor(); - mCursor = nullptr; - - MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe()); - } -} - -void -BackgroundCursorChild::HandleResponse(nsresult aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aResponse)); - MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - DispatchErrorEvent(mRequest, aResponse, mTransaction); -} - -void -BackgroundCursorChild::HandleResponse(const void_t& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - if (mCursor) { - mCursor->Reset(); - } - - ResultHelper helper(mRequest, mTransaction, &JS::NullHandleValue); - DispatchSuccessEvent(&helper); - - if (!mCursor) { - nsCOMPtr deleteRunnable = new DelayedDeleteRunnable(this); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable))); - } -} - -void -BackgroundCursorChild::HandleResponse( - const ObjectStoreCursorResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mObjectStore); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - // XXX Fix this somehow... - auto& response = const_cast(aResponse); - - StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); - - ConvertActorsToBlobs(mTransaction->Database(), - response.cloneInfo(), - cloneReadInfo.mFiles); - - nsRefPtr newCursor; - - if (mCursor) { - mCursor->Reset(Move(response.key()), Move(cloneReadInfo)); - } else { - newCursor = IDBCursor::Create(mObjectStore, - this, - mDirection, - Move(response.key()), - Move(cloneReadInfo)); - mCursor = newCursor; - } - - ResultHelper helper(mRequest, mTransaction, mCursor); - DispatchSuccessEvent(&helper); -} - -void -BackgroundCursorChild::HandleResponse( - const ObjectStoreKeyCursorResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mObjectStore); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - // XXX Fix this somehow... - auto& response = const_cast(aResponse); - - nsRefPtr newCursor; - - if (mCursor) { - mCursor->Reset(Move(response.key())); - } else { - newCursor = IDBCursor::Create(mObjectStore, - this, - mDirection, - Move(response.key())); - mCursor = newCursor; - } - - ResultHelper helper(mRequest, mTransaction, mCursor); - DispatchSuccessEvent(&helper); -} - -void -BackgroundCursorChild::HandleResponse(const IndexCursorResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mIndex); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - // XXX Fix this somehow... - auto& response = const_cast(aResponse); - - StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); - - ConvertActorsToBlobs(mTransaction->Database(), - aResponse.cloneInfo(), - cloneReadInfo.mFiles); - - nsRefPtr newCursor; - - if (mCursor) { - mCursor->Reset(Move(response.key()), - Move(response.objectKey()), - Move(cloneReadInfo)); - } else { - newCursor = IDBCursor::Create(mIndex, - this, - mDirection, - Move(response.key()), - Move(response.objectKey()), - Move(cloneReadInfo)); - mCursor = newCursor; - } - - ResultHelper helper(mRequest, mTransaction, mCursor); - DispatchSuccessEvent(&helper); -} - -void -BackgroundCursorChild::HandleResponse(const IndexKeyCursorResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mIndex); - MOZ_ASSERT(!mStrongRequest); - MOZ_ASSERT(!mStrongCursor); - - // XXX Fix this somehow... - auto& response = const_cast(aResponse); - - nsRefPtr newCursor; - - if (mCursor) { - mCursor->Reset(Move(response.key()), Move(response.objectKey())); - } else { - newCursor = IDBCursor::Create(mIndex, - this, - mDirection, - Move(response.key()), - Move(response.objectKey())); - mCursor = newCursor; - } - - ResultHelper helper(mRequest, mTransaction, mCursor); - DispatchSuccessEvent(&helper); -} - -void -BackgroundCursorChild::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest); - MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor); - - MaybeCollectGarbageOnIPCMessage(); - - if (mStrongRequest && !mStrongCursor && mTransaction) { - mTransaction->OnRequestFinished(); - } - - if (mCursor) { - mCursor->ClearBackgroundActor(); -#ifdef DEBUG - mCursor = nullptr; -#endif - } - -#ifdef DEBUG - mRequest = nullptr; - mTransaction = nullptr; - mObjectStore = nullptr; - mIndex = nullptr; -#endif -} - -bool -BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aResponse.type() != CursorResponse::T__None); - MOZ_ASSERT(mRequest); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mStrongRequest); - MOZ_ASSERT_IF(mCursor, mStrongCursor); - - MaybeCollectGarbageOnIPCMessage(); - - nsRefPtr request; - mStrongRequest.swap(request); - - nsRefPtr cursor; - mStrongCursor.swap(cursor); - - switch (aResponse.type()) { - case CursorResponse::Tnsresult: - HandleResponse(aResponse.get_nsresult()); - break; - - case CursorResponse::Tvoid_t: - HandleResponse(aResponse.get_void_t()); - break; - - case CursorResponse::TObjectStoreCursorResponse: - HandleResponse(aResponse.get_ObjectStoreCursorResponse()); - break; - - case CursorResponse::TObjectStoreKeyCursorResponse: - HandleResponse(aResponse.get_ObjectStoreKeyCursorResponse()); - break; - - case CursorResponse::TIndexCursorResponse: - HandleResponse(aResponse.get_IndexCursorResponse()); - break; - - case CursorResponse::TIndexKeyCursorResponse: - HandleResponse(aResponse.get_IndexKeyCursorResponse()); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - mTransaction->OnRequestFinished(); - - return true; -} - -// XXX This doesn't belong here. However, we're not yet porting MutableFile -// stuff to PBackground so this is necessary for the time being. -void -DispatchMutableFileResult(IDBRequest* aRequest, - nsresult aResultCode, - IDBMutableFile* aMutableFile) -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aRequest); - MOZ_ASSERT_IF(NS_SUCCEEDED(aResultCode), aMutableFile); - - if (NS_SUCCEEDED(aResultCode)) { - ResultHelper helper(aRequest, nullptr, aMutableFile); - DispatchSuccessEvent(&helper); - } else { - DispatchErrorEvent(aRequest, aResultCode); - } -} - -NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedDeleteRunnable, - nsIRunnable) - -NS_IMETHODIMP -BackgroundCursorChild:: -DelayedDeleteRunnable::Run() -{ - MOZ_ASSERT(mActor); - mActor->AssertIsOnOwningThread(); - MOZ_ASSERT(mRequest); - - mActor->SendDeleteMeInternal(); - - mActor = nullptr; - mRequest = nullptr; - - return NS_OK; -} - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h deleted file mode 100644 index 94f877f6818a..000000000000 --- a/dom/indexedDB/ActorsChild.h +++ /dev/null @@ -1,627 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_actorschild_h__ -#define mozilla_dom_indexeddb_actorschild_h__ - -#include "js/RootingAPI.h" -#include "mozilla/Attributes.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h" -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsTArray.h" - -class nsIEventTarget; -struct PRThread; - -namespace mozilla { -namespace ipc { - -class BackgroundChildImpl; - -} // namespace ipc - -namespace dom { -namespace indexedDB { - -class FileInfo; -class IDBCursor; -class IDBDatabase; -class IDBFactory; -class IDBMutableFile; -class IDBOpenDBRequest; -class IDBRequest; -class IDBTransaction; -class Key; -class PBackgroundIDBFileChild; -class PermissionRequestChild; -class PermissionRequestParent; -class SerializedStructuredCloneReadInfo; - -class BackgroundFactoryChild MOZ_FINAL - : public PBackgroundIDBFactoryChild -{ - friend class mozilla::ipc::BackgroundChildImpl; - friend class IDBFactory; - - IDBFactory* mFactory; - -#ifdef DEBUG - nsCOMPtr mOwningThread; -#endif - -public: - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - IDBFactory* - GetDOMObject() const - { - AssertIsOnOwningThread(); - return mFactory; - } - -private: - // Only created by IDBFactory. - explicit BackgroundFactoryChild(IDBFactory* aFactory); - - // Only destroyed by mozilla::ipc::BackgroundChildImpl. - ~BackgroundFactoryChild(); - - void - SendDeleteMeInternal(); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual PBackgroundIDBFactoryRequestChild* - AllocPBackgroundIDBFactoryRequestChild(const FactoryRequestParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBFactoryRequestChild( - PBackgroundIDBFactoryRequestChild* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBDatabaseChild* - AllocPBackgroundIDBDatabaseChild(const DatabaseSpec& aSpec, - PBackgroundIDBFactoryRequestChild* aRequest) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor) - MOZ_OVERRIDE; - - bool - SendDeleteMe() MOZ_DELETE; -}; - -class BackgroundDatabaseChild; - -class BackgroundRequestChildBase -{ -protected: - nsRefPtr mRequest; - -private: - bool mActorDestroyed; - -public: - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - IDBRequest* - GetDOMObject() const - { - AssertIsOnOwningThread(); - return mRequest; - } - - bool - IsActorDestroyed() const - { - AssertIsOnOwningThread(); - return mActorDestroyed; - } - -protected: - explicit BackgroundRequestChildBase(IDBRequest* aRequest); - - virtual - ~BackgroundRequestChildBase(); - - void - NoteActorDestroyed(); -}; - -class BackgroundFactoryRequestChild MOZ_FINAL - : public BackgroundRequestChildBase - , public PBackgroundIDBFactoryRequestChild -{ - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - friend class IDBFactory; - friend class BackgroundFactoryChild; - friend class BackgroundDatabaseChild; - friend class PermissionRequestChild; - friend class PermissionRequestParent; - - nsRefPtr mFactory; - const uint64_t mRequestedVersion; - const PersistenceType mPersistenceType; - const bool mIsDeleteOp; - -public: - IDBOpenDBRequest* - GetOpenDBRequest() const; - -private: - // Only created by IDBFactory. - BackgroundFactoryRequestChild(IDBFactory* aFactory, - IDBOpenDBRequest* aOpenRequest, - bool aIsDeleteOp, - uint64_t aRequestedVersion, - PersistenceType aPersistenceType); - - // Only destroyed by BackgroundFactoryChild. - ~BackgroundFactoryRequestChild(); - - bool - HandleResponse(nsresult aResponse); - - bool - HandleResponse(const OpenDatabaseRequestResponse& aResponse); - - bool - HandleResponse(const DeleteDatabaseRequestResponse& aResponse); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - Recv__delete__(const FactoryRequestResponse& aResponse) MOZ_OVERRIDE; - - virtual bool - RecvPermissionChallenge(const PrincipalInfo& aPrincipalInfo) MOZ_OVERRIDE; - - virtual bool - RecvBlocked(const uint64_t& aCurrentVersion) MOZ_OVERRIDE; -}; - -class BackgroundDatabaseChild MOZ_FINAL - : public PBackgroundIDBDatabaseChild -{ - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - friend class BackgroundFactoryChild; - friend class BackgroundFactoryRequestChild; - friend class IDBDatabase; - - nsAutoPtr mSpec; - nsRefPtr mTemporaryStrongDatabase; - BackgroundFactoryRequestChild* mOpenRequestActor; - IDBDatabase* mDatabase; - - PersistenceType mPersistenceType; - -public: - void - AssertIsOnOwningThread() const - { - static_cast(Manager())->AssertIsOnOwningThread(); - } - - const DatabaseSpec* - Spec() const - { - AssertIsOnOwningThread(); - return mSpec; - } - - IDBDatabase* - GetDOMObject() const - { - AssertIsOnOwningThread(); - return mDatabase; - } - -private: - // Only constructed by BackgroundFactoryChild. - BackgroundDatabaseChild(const DatabaseSpec& aSpec, - BackgroundFactoryRequestChild* aOpenRequest); - - // Only destroyed by BackgroundFactoryChild. - ~BackgroundDatabaseChild(); - - void - SendDeleteMeInternal(); - - void - EnsureDOMObject(); - - void - ReleaseDOMObject(); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual PBackgroundIDBDatabaseFileChild* - AllocPBackgroundIDBDatabaseFileChild( - const BlobOrInputStream& aBlobOrInputStream) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBDatabaseFileChild( - PBackgroundIDBDatabaseFileChild* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBTransactionChild* - AllocPBackgroundIDBTransactionChild( - const nsTArray& aObjectStoreNames, - const Mode& aMode) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBVersionChangeTransactionChild* - AllocPBackgroundIDBVersionChangeTransactionChild( - const uint64_t& aCurrentVersion, - const uint64_t& aRequestedVersion, - const int64_t& aNextObjectStoreId, - const int64_t& aNextIndexId) - MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBVersionChangeTransactionConstructor( - PBackgroundIDBVersionChangeTransactionChild* aActor, - const uint64_t& aCurrentVersion, - const uint64_t& aRequestedVersion, - const int64_t& aNextObjectStoreId, - const int64_t& aNextIndexId) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBVersionChangeTransactionChild( - PBackgroundIDBVersionChangeTransactionChild* aActor) - MOZ_OVERRIDE; - - virtual bool - RecvVersionChange(const uint64_t& aOldVersion, - const NullableVersion& aNewVersion) - MOZ_OVERRIDE; - - virtual bool - RecvInvalidate() MOZ_OVERRIDE; - - bool - SendDeleteMe() MOZ_DELETE; -}; - -class BackgroundVersionChangeTransactionChild; - -class BackgroundTransactionBase -{ - friend class BackgroundVersionChangeTransactionChild; - - // mTemporaryStrongTransaction is strong and is only valid until the end of - // NoteComplete() member function or until the NoteActorDestroyed() member - // function is called. - nsRefPtr mTemporaryStrongTransaction; - -protected: - // mTransaction is weak and is valid until the NoteActorDestroyed() member - // function is called. - IDBTransaction* mTransaction; - -public: -#ifdef DEBUG - virtual void - AssertIsOnOwningThread() const = 0; -#else - void - AssertIsOnOwningThread() const - { } -#endif - - IDBTransaction* - GetDOMObject() const - { - AssertIsOnOwningThread(); - return mTransaction; - } - -protected: - BackgroundTransactionBase(); - explicit BackgroundTransactionBase(IDBTransaction* aTransaction); - - virtual - ~BackgroundTransactionBase(); - - void - NoteActorDestroyed(); - - void - NoteComplete(); - -private: - // Only called by BackgroundVersionChangeTransactionChild. - void - SetDOMTransaction(IDBTransaction* aDOMObject); -}; - -class BackgroundTransactionChild MOZ_FINAL - : public BackgroundTransactionBase - , public PBackgroundIDBTransactionChild -{ - friend class BackgroundDatabaseChild; - friend class IDBDatabase; - -public: -#ifdef DEBUG - virtual void - AssertIsOnOwningThread() const MOZ_OVERRIDE; -#endif - - void - SendDeleteMeInternal(); - -private: - // Only created by IDBDatabase. - explicit BackgroundTransactionChild(IDBTransaction* aTransaction); - - // Only destroyed by BackgroundDatabaseChild. - ~BackgroundTransactionChild(); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - bool - RecvComplete(const nsresult& aResult) MOZ_OVERRIDE; - - virtual PBackgroundIDBRequestChild* - AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBCursorChild* - AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) - MOZ_OVERRIDE; - - bool - SendDeleteMe() MOZ_DELETE; -}; - -class BackgroundVersionChangeTransactionChild MOZ_FINAL - : public BackgroundTransactionBase - , public PBackgroundIDBVersionChangeTransactionChild -{ - friend class BackgroundDatabaseChild; - - IDBOpenDBRequest* mOpenDBRequest; - -public: -#ifdef DEBUG - virtual void - AssertIsOnOwningThread() const MOZ_OVERRIDE; -#endif - - void - SendDeleteMeInternal(); - -private: - // Only created by BackgroundDatabaseChild. - explicit BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest); - - // Only destroyed by BackgroundDatabaseChild. - ~BackgroundVersionChangeTransactionChild(); - - // Only called by BackgroundDatabaseChild. - void - SetDOMTransaction(IDBTransaction* aDOMObject) - { - BackgroundTransactionBase::SetDOMTransaction(aDOMObject); - } - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - bool - RecvComplete(const nsresult& aResult) MOZ_OVERRIDE; - - virtual PBackgroundIDBRequestChild* - AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBCursorChild* - AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) - MOZ_OVERRIDE; - - bool - SendDeleteMe() MOZ_DELETE; -}; - -class BackgroundRequestChild MOZ_FINAL - : public BackgroundRequestChildBase - , public PBackgroundIDBRequestChild -{ - friend class BackgroundTransactionChild; - friend class BackgroundVersionChangeTransactionChild; - - nsRefPtr mTransaction; - nsTArray> mFileInfos; - -public: - explicit BackgroundRequestChild(IDBRequest* aRequest); - - void - HoldFileInfosUntilComplete(nsTArray>& aFileInfos); - -private: - // Only destroyed by BackgroundTransactionChild or - // BackgroundVersionChangeTransactionChild. - ~BackgroundRequestChild(); - - void - MaybeFinishTransactionEarly(); - - bool - HandleResponse(nsresult aResponse); - - bool - HandleResponse(const Key& aResponse); - - bool - HandleResponse(const nsTArray& aResponse); - - bool - HandleResponse(const SerializedStructuredCloneReadInfo& aResponse); - - bool - HandleResponse(const nsTArray& aResponse); - - bool - HandleResponse(JS::Handle aResponse); - - bool - HandleResponse(uint64_t aResponse); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - Recv__delete__(const RequestResponse& aResponse) MOZ_OVERRIDE; -}; - -class BackgroundCursorChild MOZ_FINAL - : public PBackgroundIDBCursorChild -{ - friend class BackgroundTransactionChild; - friend class BackgroundVersionChangeTransactionChild; - - class DelayedDeleteRunnable; - - IDBRequest* mRequest; - IDBTransaction* mTransaction; - IDBObjectStore* mObjectStore; - IDBIndex* mIndex; - IDBCursor* mCursor; - - // These are only set while a request is in progress. - nsRefPtr mStrongRequest; - nsRefPtr mStrongCursor; - - Direction mDirection; - -#ifdef DEBUG - PRThread* mOwningThread; -#endif - -public: - BackgroundCursorChild(IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - Direction aDirection); - - BackgroundCursorChild(IDBRequest* aRequest, - IDBIndex* aIndex, - Direction aDirection); - - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - void - SendContinueInternal(const CursorRequestParams& aParams); - - void - SendDeleteMeInternal(); - -private: - // Only destroyed by BackgroundTransactionChild or - // BackgroundVersionChangeTransactionChild. - ~BackgroundCursorChild(); - - void - HandleResponse(nsresult aResponse); - - void - HandleResponse(const void_t& aResponse); - - void - HandleResponse(const ObjectStoreCursorResponse& aResponse); - - void - HandleResponse(const ObjectStoreKeyCursorResponse& aResponse); - - void - HandleResponse(const IndexCursorResponse& aResponse); - - void - HandleResponse(const IndexKeyCursorResponse& aResponse); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvResponse(const CursorResponse& aResponse) MOZ_OVERRIDE; - - // Force callers to use SendContinueInternal. - bool - SendContinue(const CursorRequestParams& aParams) MOZ_DELETE; - - bool - SendDeleteMe() MOZ_DELETE; -}; - -// XXX This doesn't belong here. However, we're not yet porting MutableFile -// stuff to PBackground so this is necessary for the time being. -void -DispatchMutableFileResult(IDBRequest* aRequest, - nsresult aResultCode, - IDBMutableFile* aMutableFile); - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_indexeddb_actorschild_h__ diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp deleted file mode 100644 index d961bdcedd51..000000000000 --- a/dom/indexedDB/ActorsParent.cpp +++ /dev/null @@ -1,17034 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ActorsParent.h" - -#include -#include "FileInfo.h" -#include "FileManager.h" -#include "IDBObjectStore.h" -#include "IDBTransaction.h" -#include "IndexedDatabase.h" -#include "IndexedDatabaseInlines.h" -#include "IndexedDatabaseManager.h" -#include "js/StructuredClone.h" -#include "js/Value.h" -#include "jsapi.h" -#include "KeyPath.h" -#include "mozilla/Attributes.h" -#include "mozilla/AppProcessChecker.h" -#include "mozilla/AutoRestore.h" -#include "mozilla/Endian.h" -#include "mozilla/LazyIdleThread.h" -#include "mozilla/Maybe.h" -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/storage.h" -#include "mozilla/unused.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/StructuredCloneTags.h" -#include "mozilla/dom/TabParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h" -#include "mozilla/dom/ipc/BlobParent.h" -#include "mozilla/dom/quota/Client.h" -#include "mozilla/dom/quota/FileStreams.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/dom/quota/StoragePrivilege.h" -#include "mozilla/dom/quota/UsageInfo.h" -#include "mozilla/ipc/BackgroundParent.h" -#include "mozilla/ipc/BackgroundUtils.h" -#include "mozilla/ipc/InputStreamParams.h" -#include "mozilla/ipc/InputStreamUtils.h" -#include "mozilla/ipc/PBackground.h" -#include "nsCharSeparatedTokenizer.h" -#include "nsClassHashtable.h" -#include "nsCOMPtr.h" -#include "nsDataHashtable.h" -#include "nsDOMFile.h" -#include "nsEscape.h" -#include "nsHashKeys.h" -#include "nsNetUtil.h" -#include "nsIAppsService.h" -#include "nsIDOMFile.h" -#include "nsIEventTarget.h" -#include "nsIFile.h" -#include "nsIFileURL.h" -#include "nsIInputStream.h" -#include "nsIInterfaceRequestor.h" -#include "nsInterfaceHashtable.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" -#include "nsIOfflineStorage.h" -#include "nsIOutputStream.h" -#include "nsIPrincipal.h" -#include "nsIScriptSecurityManager.h" -#include "nsISupports.h" -#include "nsISupportsImpl.h" -#include "nsISupportsPriority.h" -#include "nsIURI.h" -#include "nsNetUtil.h" -#include "nsPrintfCString.h" -#include "nsRefPtrHashtable.h" -#include "nsString.h" -#include "nsThreadUtils.h" -#include "nsXPCOMCID.h" -#include "PermissionRequestBase.h" -#include "ProfilerHelpers.h" -#include "ReportInternalError.h" -#include "snappy/snappy.h" -#include "TransactionThreadPool.h" - -namespace mozilla { -namespace dom { -namespace indexedDB { - -using namespace mozilla::dom::quota; -using namespace mozilla::ipc; - -#define DISABLE_ASSERTS_FOR_FUZZING 0 - -#if DISABLE_ASSERTS_FOR_FUZZING -#define ASSERT_UNLESS_FUZZING(...) do { } while (0) -#else -#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) -#endif - -namespace { - -class Cursor; -class Database; -struct DatabaseActorInfo; -class DatabaseFile; -class DatabaseOfflineStorage; -class Factory; -class OpenDatabaseOp; -class TransactionBase; -class VersionChangeTransaction; - -/******************************************************************************* - * Constants - ******************************************************************************/ - -// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major -// schema version. -static_assert(JS_STRUCTURED_CLONE_VERSION == 5, - "Need to update the major schema version."); - -// Major schema version. Bump for almost everything. -const uint32_t kMajorSchemaVersion = 17; - -// Minor schema version. Should almost always be 0 (maybe bump on release -// branches if we have to). -const uint32_t kMinorSchemaVersion = 0; - -// The schema version we store in the SQLite database is a (signed) 32-bit -// integer. The major version is left-shifted 4 bits so the max value is -// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF. -static_assert(kMajorSchemaVersion <= 0xFFFFFFF, - "Major version needs to fit in 28 bits."); -static_assert(kMinorSchemaVersion <= 0xF, - "Minor version needs to fit in 4 bits."); - -const int32_t kSQLiteSchemaVersion = - int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion); - -const int32_t kStorageProgressGranularity = 1000; - -const char kSavepointClause[] = "SAVEPOINT sp;"; - -const fallible_t fallible = fallible_t(); - -const uint32_t kFileCopyBufferSize = 32768; - -const char kJournalDirectoryName[] = "journals"; - -const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled"; - -#define IDB_PREFIX "indexedDB" -#define CHROME_PREFIX "chrome" - -const char kPermissionString[] = IDB_PREFIX; - -const char kChromeOrigin[] = CHROME_PREFIX; - -const char kPermissionStringChromeBase[] = IDB_PREFIX "-" CHROME_PREFIX "-"; -const char kPermissionStringChromeReadSuffix[] = "-read"; -const char kPermissionStringChromeWriteSuffix[] = "-write"; - -#undef CHROME_PREFIX -#undef IDB_PREFIX - -enum AppId { - kNoAppId = nsIScriptSecurityManager::NO_APP_ID, - kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID -}; - -#ifdef DEBUG - -const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; -const uint32_t kDEBUGThreadSleepMS = 0; - -#endif - -/******************************************************************************* - * Metadata classes - ******************************************************************************/ - -struct FullIndexMetadata -{ - IndexMetadata mCommonMetadata; - - bool mDeleted; - -public: - FullIndexMetadata() - : mCommonMetadata(0, nsString(), KeyPath(0), false, false) - , mDeleted(false) - { - // This can happen either on the QuotaManager IO thread or on a - // versionchange transaction thread. These threads can never race so this is - // totally safe. - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata) - -private: - ~FullIndexMetadata() - { } -}; - -typedef nsRefPtrHashtable IndexTable; - -struct FullObjectStoreMetadata -{ - ObjectStoreMetadata mCommonMetadata; - IndexTable mIndexes; - - // These two members are only ever touched on a transaction thread! - int64_t mNextAutoIncrementId; - int64_t mComittedAutoIncrementId; - - bool mDeleted; - -public: - FullObjectStoreMetadata() - : mCommonMetadata(0, nsString(), KeyPath(0), false) - , mNextAutoIncrementId(0) - , mComittedAutoIncrementId(0) - , mDeleted(false) - { - // This can happen either on the QuotaManager IO thread or on a - // versionchange transaction thread. These threads can never race so this is - // totally safe. - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata); - -private: - ~FullObjectStoreMetadata() - { } -}; - -typedef nsRefPtrHashtable - ObjectStoreTable; - -struct FullDatabaseMetadata -{ - DatabaseMetadata mCommonMetadata; - nsCString mDatabaseId; - nsString mFilePath; - ObjectStoreTable mObjectStores; - - int64_t mNextObjectStoreId; - int64_t mNextIndexId; - -public: - explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata) - : mCommonMetadata(aCommonMetadata) - , mNextObjectStoreId(0) - , mNextIndexId(0) - { - AssertIsOnBackgroundThread(); - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata) - - already_AddRefed - Duplicate() const; - -private: - ~FullDatabaseMetadata() - { } -}; - -template -class MOZ_STACK_CLASS MetadataNameOrIdMatcher MOZ_FINAL -{ - typedef MetadataNameOrIdMatcher SelfType; - - const int64_t mId; - const nsString mName; - nsRefPtr mMetadata; - bool mCheckName; - -public: - template - static MetadataType* - Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aId); - - SelfType closure(aId, aName); - aEnumerable.EnumerateRead(Enumerate, &closure); - - return closure.mMetadata; - } - - template - static MetadataType* - Match(const Enumerable& aEnumerable, uint64_t aId) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aId); - - SelfType closure(aId); - aEnumerable.EnumerateRead(Enumerate, &closure); - - return closure.mMetadata; - } - -private: - MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName) - : mId(aId) - , mName(PromiseFlatString(aName)) - , mMetadata(nullptr) - , mCheckName(true) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aId); - } - - explicit MetadataNameOrIdMatcher(const int64_t& aId) - : mId(aId) - , mMetadata(nullptr) - , mCheckName(false) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aId); - } - - static PLDHashOperator - Enumerate(const uint64_t& aKey, MetadataType* aValue, void* aClosure) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* closure = static_cast(aClosure); - - if (!aValue->mDeleted && - (closure->mId == aValue->mCommonMetadata.id() || - (closure->mCheckName && - closure->mName == aValue->mCommonMetadata.name()))) { - closure->mMetadata = aValue; - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; - } -}; - -/******************************************************************************* - * SQLite functions - ******************************************************************************/ - -int32_t -MakeSchemaVersion(uint32_t aMajorSchemaVersion, - uint32_t aMinorSchemaVersion) -{ - return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion); -} - -uint32_t -HashName(const nsAString& aName) -{ - struct Helper - { - static uint32_t - RotateBitsLeft32(uint32_t aValue, uint8_t aBits) - { - MOZ_ASSERT(aBits < 32); - return (aValue << aBits) | (aValue >> (32 - aBits)); - } - }; - - static const uint32_t kGoldenRatioU32 = 0x9e3779b9u; - - const char16_t* str = aName.BeginReading(); - size_t length = aName.Length(); - - uint32_t hash = 0; - for (size_t i = 0; i < length; i++) { - hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]); - } - - return hash; -} - -nsresult -ClampResultCode(nsresult aResultCode) -{ - if (NS_SUCCEEDED(aResultCode) || - NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) { - return aResultCode; - } - - switch (aResultCode) { - case NS_ERROR_FILE_NO_DEVICE_SPACE: - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - case NS_ERROR_STORAGE_CONSTRAINT: - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - default: -#ifdef DEBUG - nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to " - "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR", - aResultCode); - NS_WARNING(message.get()); -#else - ; -#endif - } - - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; -} - -void -GetDatabaseFilename(const nsAString& aName, - nsAutoString& aDatabaseFilename) -{ - MOZ_ASSERT(aDatabaseFilename.IsEmpty()); - - aDatabaseFilename.AppendInt(HashName(aName)); - - nsCString escapedName; - if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { - MOZ_CRASH("Can't escape database name!"); - } - - const char* forwardIter = escapedName.BeginReading(); - const char* backwardIter = escapedName.EndReading() - 1; - - nsAutoCString substring; - while (forwardIter <= backwardIter && substring.Length() < 21) { - if (substring.Length() % 2) { - substring.Append(*backwardIter--); - } else { - substring.Append(*forwardIter++); - } - } - - aDatabaseFilename.AppendASCII(substring.get(), substring.Length()); -} - -nsresult -CreateFileTables(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "CreateFileTables", - js::ProfileEntry::Category::STORAGE); - - // Table `file` - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE file (" - "id INTEGER PRIMARY KEY, " - "refcount INTEGER NOT NULL" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_insert_trigger " - "AFTER INSERT ON object_data " - "FOR EACH ROW " - "WHEN NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(NULL, NEW.file_ids); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_update_trigger " - "AFTER UPDATE OF file_ids ON object_data " - "FOR EACH ROW " - "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_delete_trigger " - "AFTER DELETE ON object_data " - "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NULL); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER file_update_trigger " - "AFTER UPDATE ON file " - "FOR EACH ROW WHEN NEW.refcount = 0 " - "BEGIN " - "DELETE FROM file WHERE id = OLD.id; " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -CreateTables(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "CreateTables", - js::ProfileEntry::Category::STORAGE); - - // Table `database` - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE database (" - "name TEXT NOT NULL, " - "version INTEGER NOT NULL DEFAULT 0" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Table `object_store` - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store (" - "id INTEGER PRIMARY KEY, " - "auto_increment INTEGER NOT NULL DEFAULT 0, " - "name TEXT NOT NULL, " - "key_path TEXT, " - "UNIQUE (name)" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Table `object_data` - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "key_value BLOB DEFAULT NULL, " - "file_ids TEXT, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, key_value), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Table `index` - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Table `index_data` - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Need this to make cascading deletes from object_data and object_store fast. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX index_data_object_data_id_index " - "ON index_data (object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Table `unique_index_data` - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Need this to make cascading deletes from object_data and object_store fast. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX unique_index_data_object_data_id_index " - "ON unique_index_data (object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = CreateFileTables(aConnection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom4To5", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - - // All we changed is the type of the version column, so lets try to - // convert that to an integer, and if we fail, set it to 0. - nsCOMPtr stmt; - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name, version, dataVersion " - "FROM database" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsString name; - int32_t intVersion; - int64_t dataVersion; - - { - mozStorageStatementScoper scoper(stmt); - - bool hasResults; - rv = stmt->ExecuteStep(&hasResults); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - if (NS_WARN_IF(!hasResults)) { - return NS_ERROR_FAILURE; - } - - nsString version; - rv = stmt->GetString(1, version); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - intVersion = version.ToInteger(&rv); - if (NS_FAILED(rv)) { - intVersion = 0; - } - - rv = stmt->GetString(0, name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->GetInt64(2, &dataVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE database" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE database (" - "name TEXT NOT NULL, " - "version INTEGER NOT NULL DEFAULT 0, " - "dataVersion INTEGER NOT NULL" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO database (name, version, dataVersion) " - "VALUES (:name, :version, :dataVersion)" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - { - mozStorageStatementScoper scoper(stmt); - - rv = stmt->BindStringParameter(0, name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt32Parameter(1, intVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64Parameter(2, dataVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = aConnection->SetSchemaVersion(5); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom5To6", - js::ProfileEntry::Category::STORAGE); - - // First, drop all the indexes we're no longer going to use. - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX key_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX ai_key_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX value_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX ai_value_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Now, reorder the columns of object_data to put the blob data last. We do - // this by copying into a temporary table, dropping the original, then copying - // back into a newly created table. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id INTEGER PRIMARY KEY, " - "object_store_id, " - "key_value, " - "data " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, key_value, data " - "FROM object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "key_value DEFAULT NULL, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, key_value), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data " - "SELECT id, object_store_id, key_value, data " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // We need to add a unique constraint to our ai_object_data table. Copy all - // the data out of it using a temporary table as before. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id INTEGER PRIMARY KEY, " - "object_store_id, " - "data " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, data " - "FROM ai_object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_object_data (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "object_store_id INTEGER NOT NULL, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, id), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO ai_object_data " - "SELECT id, object_store_id, data " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Fix up the index_data table. We're reordering the columns as well as - // changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "object_data_key NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT OR IGNORE INTO index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX index_data_object_data_id_index " - "ON index_data (object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Fix up the unique_index_data table. We're reordering the columns as well as - // changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "object_data_key NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX unique_index_data_object_data_id_index " - "ON unique_index_data (object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Fix up the ai_index_data table. We're reordering the columns as well as - // changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "ai_object_data_id " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, ai_object_data_id " - "FROM ai_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "ai_object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, ai_object_data_id), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT OR IGNORE INTO ai_index_data " - "SELECT index_id, value, ai_object_data_id " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX ai_index_data_ai_object_data_id_index " - "ON ai_index_data (ai_object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Fix up the ai_unique_index_data table. We're reordering the columns as well - // as changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "ai_object_data_id " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, ai_object_data_id " - "FROM ai_unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_unique_index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "ai_object_data_id INTEGER NOT NULL, " - "UNIQUE (index_id, value), " - "PRIMARY KEY (index_id, value, ai_object_data_id), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO ai_unique_index_data " - "SELECT index_id, value, ai_object_data_id " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX ai_unique_index_data_ai_object_data_id_index " - "ON ai_unique_index_data (ai_object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(6); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom6To7", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "name, " - "key_path, " - "auto_increment" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, name, key_path, auto_increment " - "FROM object_store;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_store;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store (" - "id INTEGER PRIMARY KEY, " - "auto_increment INTEGER NOT NULL DEFAULT 0, " - "name TEXT NOT NULL, " - "key_path TEXT, " - "UNIQUE (name)" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_store " - "SELECT id, auto_increment, name, nullif(key_path, '') " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(7); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom7To8", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "object_store_id, " - "name, " - "key_path, " - "unique_index, " - "object_store_autoincrement" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, name, key_path, " - "unique_index, object_store_autoincrement " - "FROM object_store_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_store_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "object_store_autoincrement INTERGER NOT NULL, " - "PRIMARY KEY (id), " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_store_index " - "SELECT id, object_store_id, name, key_path, " - "unique_index, 0, object_store_autoincrement " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(8); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -class CompressDataBlobsFunction MOZ_FINAL - : public mozIStorageFunction -{ -public: - NS_DECL_ISUPPORTS - -private: - ~CompressDataBlobsFunction() - { } - - NS_IMETHOD - OnFunctionCall(mozIStorageValueArray* aArguments, - nsIVariant** aResult) MOZ_OVERRIDE - { - MOZ_ASSERT(aArguments); - MOZ_ASSERT(aResult); - - PROFILER_LABEL("IndexedDB", - "CompressDataBlobsFunction::OnFunctionCall", - js::ProfileEntry::Category::STORAGE); - - uint32_t argc; - nsresult rv = aArguments->GetNumEntries(&argc); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (argc != 1) { - NS_WARNING("Don't call me with the wrong number of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - int32_t type; - rv = aArguments->GetTypeOfIndex(0, &type); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (type != mozIStorageStatement::VALUE_TYPE_BLOB) { - NS_WARNING("Don't call me with the wrong type of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - const uint8_t* uncompressed; - uint32_t uncompressedLength; - rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); - nsAutoArrayPtr compressed(new (fallible) char[compressedLength]); - if (NS_WARN_IF(!compressed)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - snappy::RawCompress(reinterpret_cast(uncompressed), - uncompressedLength, compressed.get(), - &compressedLength); - - std::pair data(static_cast(compressed.get()), - int(compressedLength)); - - // XXX This copies the buffer again... There doesn't appear to be any way to - // preallocate space and write directly to a BlobVariant at the moment. - nsCOMPtr result = new mozilla::storage::BlobVariant(data); - - result.forget(aResult); - return NS_OK; - } -}; - -nsresult -UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom8To9_0", - js::ProfileEntry::Category::STORAGE); - - // We no longer use the dataVersion column. - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE database SET dataVersion = 0;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr compressor = new CompressDataBlobsFunction(); - - NS_NAMED_LITERAL_CSTRING(compressorName, "compress"); - - rv = aConnection->CreateFunction(compressorName, 1, compressor); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Turn off foreign key constraints before we do anything here. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE object_data SET data = compress(data);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE ai_object_data SET data = compress(data);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->RemoveFunction(compressorName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom9_0To10_0", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE object_data ADD COLUMN file_ids TEXT;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = CreateFileTables(aConnection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom10_0To11_0", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "object_store_id, " - "name, " - "key_path, " - "unique_index, " - "multientry" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, name, key_path, " - "unique_index, multientry " - "FROM object_store_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_store_index;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_store_index " - "SELECT id, object_store_id, name, key_path, " - "unique_index, multientry " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TRIGGER object_data_insert_trigger;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " - "SELECT object_store_id, id, data, file_ids " - "FROM ai_object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_insert_trigger " - "AFTER INSERT ON object_data " - "FOR EACH ROW " - "WHEN NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(NULL, NEW.file_ids); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) " - "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id " - "FROM ai_index_data " - "INNER JOIN object_store_index ON " - "object_store_index.id = ai_index_data.index_id " - "INNER JOIN object_data ON " - "object_data.object_store_id = object_store_index.object_store_id AND " - "object_data.key_value = ai_index_data.ai_object_data_id;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) " - "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id " - "FROM ai_unique_index_data " - "INNER JOIN object_store_index ON " - "object_store_index.id = ai_unique_index_data.index_id " - "INNER JOIN object_data ON " - "object_data.object_store_id = object_store_index.object_store_id AND " - "object_data.key_value = ai_unique_index_data.ai_object_data_id;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE object_store " - "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 " - "WHERE auto_increment;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -class EncodeKeysFunction MOZ_FINAL - : public mozIStorageFunction -{ -public: - NS_DECL_ISUPPORTS - -private: - ~EncodeKeysFunction() - { } - - NS_IMETHOD - OnFunctionCall(mozIStorageValueArray* aArguments, - nsIVariant** aResult) MOZ_OVERRIDE - { - MOZ_ASSERT(aArguments); - MOZ_ASSERT(aResult); - - PROFILER_LABEL("IndexedDB", - "EncodeKeysFunction::OnFunctionCall", - js::ProfileEntry::Category::STORAGE); - - uint32_t argc; - nsresult rv = aArguments->GetNumEntries(&argc); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (argc != 1) { - NS_WARNING("Don't call me with the wrong number of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - int32_t type; - rv = aArguments->GetTypeOfIndex(0, &type); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - Key key; - if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) { - int64_t intKey; - aArguments->GetInt64(0, &intKey); - key.SetFromInteger(intKey); - } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) { - nsString stringKey; - aArguments->GetString(0, stringKey); - key.SetFromString(stringKey); - } else { - NS_WARNING("Don't call me with the wrong type of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - const nsCString& buffer = key.GetBuffer(); - - std::pair data(static_cast(buffer.get()), - int(buffer.Length())); - - nsCOMPtr result = new mozilla::storage::BlobVariant(data); - - result.forget(aResult); - return NS_OK; - } -}; - -nsresult -UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom11_0To12_0", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(encoderName, "encode"); - - nsCOMPtr encoder = new EncodeKeysFunction(); - - nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id INTEGER PRIMARY KEY, " - "object_store_id, " - "key_value, " - "data, " - "file_ids " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, encode(key_value), data, file_ids " - "FROM object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "key_value BLOB DEFAULT NULL, " - "file_ids TEXT, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, key_value), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data " - "SELECT id, object_store_id, key_value, file_ids, data " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_insert_trigger " - "AFTER INSERT ON object_data " - "FOR EACH ROW " - "WHEN NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(NULL, NEW.file_ids); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_update_trigger " - "AFTER UPDATE OF file_ids ON object_data " - "FOR EACH ROW " - "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_delete_trigger " - "AFTER DELETE ON object_data " - "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NULL); " - "END;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, encode(value), encode(object_data_key), object_data_id " - "FROM index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX index_data_object_data_id_index " - "ON index_data (object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, encode(value), encode(object_data_key), object_data_id " - "FROM unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE unique_index_data;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX unique_index_data_object_data_id_index " - "ON unique_index_data (object_data_id);" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->RemoveFunction(encoderName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection, - bool* aVacuumNeeded) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "UpgradeSchemaFrom12_0To13_0", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - -#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) - int32_t defaultPageSize; - rv = aConnection->GetDefaultPageSize(&defaultPageSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Enable auto_vacuum mode and update the page size to the platform default. - nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = "); - upgradeQuery.AppendInt(defaultPageSize); - - rv = aConnection->ExecuteSimpleSQL(upgradeQuery); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - *aVacuumNeeded = true; -#endif - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - - // The only change between 13 and 14 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection) -{ - // The only change between 14 and 15 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection) -{ - // The only change between 15 and 16 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection) -{ - // The only change between 16 and 17 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -GetDatabaseFileURL(nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - nsIFileURL** aResult) -{ - MOZ_ASSERT(aDatabaseFile); - MOZ_ASSERT(aResult); - - nsCOMPtr uri; - nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr fileUrl = do_QueryInterface(uri); - MOZ_ASSERT(fileUrl); - - nsAutoCString type; - PersistenceTypeToText(aPersistenceType, type); - - rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + - NS_LITERAL_CSTRING("&group=") + aGroup + - NS_LITERAL_CSTRING("&origin=") + aOrigin); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - fileUrl.forget(aResult); - return NS_OK; -} - -nsresult -SetDefaultPragmas(mozIStorageConnection* aConnection) -{ - MOZ_ASSERT(aConnection); - - static const char query[] = -#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) - // Switch the journaling mode to TRUNCATE to avoid changing the directory - // structure at the conclusion of every transaction for devices with slower - // file systems. - "PRAGMA journal_mode = TRUNCATE; " -#endif - // We use foreign keys in lots of places. - "PRAGMA foreign_keys = ON; " - // The "INSERT OR REPLACE" statement doesn't fire the update trigger, - // instead it fires only the insert trigger. This confuses the update - // refcount function. This behavior changes with enabled recursive triggers, - // so the statement fires the delete trigger first and then the insert - // trigger. - "PRAGMA recursive_triggers = ON;"; - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -CreateDatabaseConnection(nsIFile* aDBFile, - nsIFile* aFMDirectory, - const nsAString& aName, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - mozIStorageConnection** aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aDBFile); - MOZ_ASSERT(aFMDirectory); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "CreateDatabaseConnection", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - bool exists; - - if (IndexedDatabaseManager::InLowDiskSpaceMode()) { - rv = aDBFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!exists) { - NS_WARNING("Refusing to create database because disk space is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - } - - nsCOMPtr dbFileUrl; - rv = GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin, - getter_AddRefs(dbFileUrl)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr connection; - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); - if (rv == NS_ERROR_FILE_CORRUPTED) { - // If we're just opening the database during origin initialization, then - // we don't want to erase any files. The failure here will fail origin - // initialization too. - if (aName.IsVoid()) { - return rv; - } - - // Nuke the database file. - rv = aDBFile->Remove(false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aFMDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - bool isDirectory; - rv = aFMDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - if (NS_WARN_IF(!isDirectory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - rv = aFMDirectory->Remove(true); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = SetDefaultPragmas(connection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Check to make sure that the database schema is correct. - int32_t schemaVersion; - rv = connection->GetSchemaVersion(&schemaVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Unknown schema will fail origin initialization too. - if (!schemaVersion && aName.IsVoid()) { - IDB_WARNING("Unable to open IndexedDB database, schema is not set!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (schemaVersion > kSQLiteSchemaVersion) { - IDB_WARNING("Unable to open IndexedDB database, schema is too high!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - bool vacuumNeeded = false; - - if (schemaVersion != kSQLiteSchemaVersion) { -#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) - if (!schemaVersion) { - // Have to do this before opening a transaction. - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - // Turn on auto_vacuum mode to reclaim disk space on mobile devices. - "PRAGMA auto_vacuum = FULL; " - )); - if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, - // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. - rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } -#endif - - mozStorageTransaction transaction(connection, false, - mozIStorageConnection::TRANSACTION_IMMEDIATE); - - if (!schemaVersion) { - // Brand new file, initialize our tables. - rv = CreateTables(connection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion))); - MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion); - - nsCOMPtr stmt; - nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO database (name) " - "VALUES (:name)" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - // This logic needs to change next time we change the schema! - static_assert(kSQLiteSchemaVersion == int32_t((17 << 4) + 0), - "Upgrade function needed due to schema version increase."); - - while (schemaVersion != kSQLiteSchemaVersion) { - if (schemaVersion == 4) { - rv = UpgradeSchemaFrom4To5(connection); - } else if (schemaVersion == 5) { - rv = UpgradeSchemaFrom5To6(connection); - } else if (schemaVersion == 6) { - rv = UpgradeSchemaFrom6To7(connection); - } else if (schemaVersion == 7) { - rv = UpgradeSchemaFrom7To8(connection); - } else if (schemaVersion == 8) { - rv = UpgradeSchemaFrom8To9_0(connection); - vacuumNeeded = true; - } else if (schemaVersion == MakeSchemaVersion(9, 0)) { - rv = UpgradeSchemaFrom9_0To10_0(connection); - } else if (schemaVersion == MakeSchemaVersion(10, 0)) { - rv = UpgradeSchemaFrom10_0To11_0(connection); - } else if (schemaVersion == MakeSchemaVersion(11, 0)) { - rv = UpgradeSchemaFrom11_0To12_0(connection); - } else if (schemaVersion == MakeSchemaVersion(12, 0)) { - rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded); - } else if (schemaVersion == MakeSchemaVersion(13, 0)) { - rv = UpgradeSchemaFrom13_0To14_0(connection); - } else if (schemaVersion == MakeSchemaVersion(14, 0)) { - rv = UpgradeSchemaFrom14_0To15_0(connection); - } else if (schemaVersion == MakeSchemaVersion(15, 0)) { - rv = UpgradeSchemaFrom15_0To16_0(connection); - } else if (schemaVersion == MakeSchemaVersion(16, 0)) { - rv = UpgradeSchemaFrom16_0To17_0(connection); - } else { - IDB_WARNING("Unable to open IndexedDB database, no upgrade path is " - "available!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = connection->GetSchemaVersion(&schemaVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion); - } - - rv = transaction.Commit(); - if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, - // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. - rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (vacuumNeeded) { - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - connection.forget(aConnection); - return NS_OK; -} - -already_AddRefed -GetFileForPath(const nsAString& aPath) -{ - MOZ_ASSERT(!aPath.IsEmpty()); - - nsCOMPtr file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); - if (NS_WARN_IF(!file)) { - return nullptr; - } - - if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) { - return nullptr; - } - - return file.forget(); -} - -nsresult -GetDatabaseConnection(const nsAString& aDatabaseFilePath, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - mozIStorageConnection** aConnection) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(!aDatabaseFilePath.IsEmpty()); - MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite"))); - MOZ_ASSERT(aConnection); - - PROFILER_LABEL("IndexedDB", - "GetDatabaseConnection", - js::ProfileEntry::Category::STORAGE); - - nsCOMPtr dbFile = GetFileForPath(aDatabaseFilePath); - if (NS_WARN_IF(!dbFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - bool exists; - nsresult rv = dbFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!exists)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr dbFileUrl; - rv = GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin, - getter_AddRefs(dbFileUrl)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr connection; - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = SetDefaultPragmas(connection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - connection.forget(aConnection); - return NS_OK; -} - -/******************************************************************************* - * Actor class declarations - ******************************************************************************/ - -class DatabaseOperationBase - : public nsRunnable - , public mozIStorageProgressHandler -{ - // Uniquely tracks each operation for logging purposes. Only modified on the - // PBackground thread. - static uint64_t sNextSerialNumber; - -protected: - class AutoSetProgressHandler; - - typedef nsDataHashtable UniqueIndexTable; - - nsCOMPtr mOwningThread; - const uint64_t mSerialNumber; - nsresult mResultCode; - -private: - Atomic mOperationMayProceed; - bool mActorDestroyed; - -public: - NS_DECL_ISUPPORTS_INHERITED - - void - AssertIsOnOwningThread() const - { - AssertIsOnBackgroundThread(); - -#ifdef DEBUG - MOZ_ASSERT(mOwningThread); - bool current; - MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); - MOZ_ASSERT(current); -#endif - } - - void - NoteActorDestroyed() - { - AssertIsOnOwningThread(); - - mActorDestroyed = true; - mOperationMayProceed = false; - } - - bool - IsActorDestroyed() const - { - AssertIsOnOwningThread(); - - return mActorDestroyed; - } - - // May be called on any thread. - bool - OperationMayProceed() const - { - return mOperationMayProceed; - } - - uint64_t - SerialNumber() const - { - return mSerialNumber; - } - - nsresult - ResultCode() const - { - return mResultCode; - } - - void - SetFailureCode(nsresult aErrorCode) - { - MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); - MOZ_ASSERT(NS_FAILED(aErrorCode)); - - mResultCode = aErrorCode; - } - -protected: - DatabaseOperationBase() - : mOwningThread(NS_GetCurrentThread()) - , mSerialNumber(++sNextSerialNumber) - , mResultCode(NS_OK) - , mOperationMayProceed(true) - , mActorDestroyed(false) - { - AssertIsOnOwningThread(); - } - - virtual - ~DatabaseOperationBase() - { - MOZ_ASSERT(mActorDestroyed); - } - - static void - GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange, - const nsACString& aKeyColumnName, - nsAutoCString& aBindingClause); - - static uint64_t - ReinterpretDoubleAsUInt64(double aDouble); - - static nsresult - GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement, - uint32_t aDataIndex, - uint32_t aFileIdsIndex, - FileManager* aFileManager, - StructuredCloneReadInfo* aInfo); - - static nsresult - BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange, - mozIStorageStatement* aStatement); - - static void - AppendConditionClause(const nsACString& aColumnName, - const nsACString& aArgName, - bool aLessThan, - bool aEquals, - nsAutoCString& aResult); - - static nsresult - UpdateIndexes(TransactionBase* aTransaction, - const UniqueIndexTable& aUniqueIndexTable, - const Key& aObjectStoreKey, - bool aOverwrite, - int64_t aObjectDataId, - const nsTArray& aUpdateInfoArray); - -private: - // Not to be overridden by subclasses. - NS_DECL_MOZISTORAGEPROGRESSHANDLER -}; - -class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler MOZ_FINAL -{ - mozIStorageConnection* mConnection; - DebugOnly mDEBUGDatabaseOp; - -public: - AutoSetProgressHandler() - : mConnection(nullptr) - , mDEBUGDatabaseOp(nullptr) - { } - - ~AutoSetProgressHandler(); - - nsresult - Register(DatabaseOperationBase* aDatabaseOp, - const nsCOMPtr& aConnection); -}; - -class TransactionDatabaseOperationBase - : public DatabaseOperationBase -{ - nsRefPtr mTransaction; - const bool mTransactionIsAborted; - -public: - void - AssertIsOnTransactionThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - void - DispatchToTransactionThreadPool(); - - // May be overridden by subclasses if they need to perform work on the - // background thread before being dispatched. Returning false will kill the - // child actors and prevent dispatch. - virtual bool - Init(TransactionBase* aTransaction); - - // This callback will be called on the background thread before releasing the - // final reference to this request object. Subclasses may perform any - // additional cleanup here but must always call the base class implementation. - virtual void - Cleanup(); - -protected: - explicit TransactionDatabaseOperationBase(TransactionBase* aTransaction); - - virtual - ~TransactionDatabaseOperationBase(); - - // Must be overridden in subclasses. Called on the target thread to allow the - // subclass to perform necessary database or file operations. A successful - // return value will trigger a SendSuccessResult callback on the background - // thread while a failure value will trigger a SendFailureResult callback. - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) = 0; - - // Must be overridden in subclasses. Called on the background thread to allow - // the subclass to serialize its results and send them to the child actor. A - // failed return value will trigger a SendFailureResult callback. - virtual nsresult - SendSuccessResult() = 0; - - // Must be overridden in subclasses. Called on the background thread to allow - // the subclass to send its failure code. Returning false will cause the - // transaction to be aborted with aResultCode. Returning true will not cause - // the transaction to be aborted. - virtual bool - SendFailureResult(nsresult aResultCode) = 0; - -private: - void - RunOnTransactionThread(); - - void - RunOnOwningThread(); - - // Not to be overridden by subclasses. - NS_DECL_NSIRUNNABLE -}; - -class Factory MOZ_FINAL - : public PBackgroundIDBFactoryParent -{ - // Counts the number of "live" Factory instances that have not yet had - // ActorDestroy called. - static uint64_t sFactoryInstanceCount; - - nsRefPtr mTransactionThreadPool; - - const OptionalWindowId mOptionalWindowId; - - DebugOnly mActorDestroyed; - -public: - static already_AddRefed - Create(const OptionalWindowId& aOptionalWindowId); - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory) - - TransactionThreadPool* - GetTransactionThreadPool() const - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransactionThreadPool); - - return mTransactionThreadPool; - } - -private: - // Only constructed in Create(). - explicit Factory(const OptionalWindowId& aOptionalWindowId); - - // Reference counted. - ~Factory(); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvDeleteMe() MOZ_OVERRIDE; - - virtual PBackgroundIDBFactoryRequestParent* - AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams) - MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBFactoryRequestConstructor( - PBackgroundIDBFactoryRequestParent* aActor, - const FactoryRequestParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBFactoryRequestParent( - PBackgroundIDBFactoryRequestParent* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBDatabaseParent* - AllocPBackgroundIDBDatabaseParent( - const DatabaseSpec& aSpec, - PBackgroundIDBFactoryRequestParent* aRequest) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent* aActor) - MOZ_OVERRIDE; -}; - -class Database MOZ_FINAL - : public PBackgroundIDBDatabaseParent -{ - friend class VersionChangeTransaction; - - nsRefPtr mFactory; - nsRefPtr mTransactionThreadPool; - nsRefPtr mMetadata; - nsRefPtr mFileManager; - nsRefPtr mOfflineStorage; - nsTHashtable> mTransactions; - const PrincipalInfo mPrincipalInfo; - const nsCString mGroup; - const nsCString mOrigin; - const nsCString mId; - const nsString mFilePath; - Atomic mInvalidatedOnAnyThread; - const PersistenceType mPersistenceType; - const bool mChromeWriteAccessAllowed; - bool mClosed; - bool mInvalidated; - bool mActorWasAlive; - bool mActorDestroyed; - bool mMetadataCleanedUp; - -public: - // Created by OpenDatabaseOp. - Database(Factory* aFactory, - const PrincipalInfo& aPrincipalInfo, - const nsACString& aGroup, - const nsACString& aOrigin, - FullDatabaseMetadata* aMetadata, - FileManager* aFileManager, - already_AddRefed aOfflineStorage, - bool aChromeWriteAccessAllowed); - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database) - - void - Invalidate(); - - const PrincipalInfo& - GetPrincipalInfo() const - { - return mPrincipalInfo; - } - - const nsCString& - Group() const - { - return mGroup; - } - - const nsCString& - Origin() const - { - return mOrigin; - } - - const nsCString& - Id() const - { - return mId; - } - - PersistenceType - Type() const - { - return mPersistenceType; - } - - const nsString& - FilePath() const - { - return mFilePath; - } - - FileManager* - GetFileManager() const - { - return mFileManager; - } - - FullDatabaseMetadata* - Metadata() const - { - MOZ_ASSERT(mMetadata); - return mMetadata; - } - - PBackgroundParent* - GetBackgroundParent() const - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!IsActorDestroyed()); - - return Manager()->Manager(); - } - - TransactionThreadPool* - GetTransactionThreadPool() const - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransactionThreadPool); - - return mTransactionThreadPool; - } - - bool - RegisterTransaction(TransactionBase* aTransaction); - - void - UnregisterTransaction(TransactionBase* aTransaction); - - void - SetActorAlive(); - - bool - IsActorAlive() const - { - AssertIsOnBackgroundThread(); - - return mActorWasAlive && !mActorDestroyed; - } - - bool - IsActorDestroyed() const - { - AssertIsOnBackgroundThread(); - - return mActorWasAlive && mActorDestroyed; - } - - bool - IsClosed() const - { - AssertIsOnBackgroundThread(); - - return mClosed; - } - - bool - IsInvalidated() const - { - AssertIsOnBackgroundThread(); - - return mInvalidated; - } - -private: - // Reference counted. - ~Database() - { - MOZ_ASSERT(mClosed); - MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); - } - - bool - CloseInternal(); - - void - CleanupMetadata(); - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual PBackgroundIDBDatabaseFileParent* - AllocPBackgroundIDBDatabaseFileParent( - const BlobOrInputStream& aBlobOrInputStream) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBDatabaseFileParent( - PBackgroundIDBDatabaseFileParent* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBTransactionParent* - AllocPBackgroundIDBTransactionParent( - const nsTArray& aObjectStoreNames, - const Mode& aMode) - MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBTransactionConstructor( - PBackgroundIDBTransactionParent* aActor, - const nsTArray& aObjectStoreNames, - const Mode& aMode) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBTransactionParent( - PBackgroundIDBTransactionParent* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBVersionChangeTransactionParent* - AllocPBackgroundIDBVersionChangeTransactionParent( - const uint64_t& aCurrentVersion, - const uint64_t& aRequestedVersion, - const int64_t& aNextObjectStoreId, - const int64_t& aNextIndexId) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBVersionChangeTransactionParent( - PBackgroundIDBVersionChangeTransactionParent* aActor) - MOZ_OVERRIDE; - - virtual bool - RecvDeleteMe() MOZ_OVERRIDE; - - virtual bool - RecvBlocked() MOZ_OVERRIDE; - - virtual bool - RecvClose() MOZ_OVERRIDE; -}; - -class DatabaseFile MOZ_FINAL - : public PBackgroundIDBDatabaseFileParent -{ - friend class Database; - - InputStreamParams mInputStreamParams; - nsRefPtr mFileInfo; - -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile); - - FileInfo* - GetFileInfo() const - { - AssertIsOnBackgroundThread(); - - return mFileInfo; - } - - already_AddRefed - GetInputStream() const; - - void - ClearInputStreamParams(); - -private: - // Called when sending to the child. - explicit DatabaseFile(FileInfo* aFileInfo) - : mFileInfo(aFileInfo) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aFileInfo); - } - - // Called when receiving from the child. - DatabaseFile(const InputStreamParams& aInputStreamParams, FileInfo* aFileInfo) - : mInputStreamParams(aInputStreamParams) - , mFileInfo(aFileInfo) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aInputStreamParams.type() != InputStreamParams::T__None); - MOZ_ASSERT(aFileInfo); - } - - ~DatabaseFile() - { } - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; -}; - -class TransactionBase -{ - friend class Cursor; - - class CommitOp; - class UpdateRefcountFunction; - -public: - class AutoSavepoint; - class CachedStatement; - -protected: - typedef IDBTransaction::Mode Mode; - -private: - nsRefPtr mDatabase; - nsRefPtr mTransactionThreadPool; - nsCOMPtr mConnection; - nsRefPtr mUpdateFileRefcountFunction; - nsInterfaceHashtable - mCachedStatements; - nsTArray> - mModifiedAutoIncrementObjectStoreMetadataArray; - const uint64_t mTransactionId; - const nsCString mDatabaseId; - uint64_t mActiveRequestCount; - Atomic mInvalidatedOnAnyThread; - Mode mMode; - bool mHasBeenActive; - bool mActorDestroyed; - bool mInvalidated; - -protected: - nsresult mResultCode; - bool mCommitOrAbortReceived; - bool mCommittedOrAborted; - bool mForceAborted; - -private: - DebugOnly mTransactionThread; - DebugOnly mSavepointCount; - -public: - void - AssertIsOnTransactionThread() const - { - MOZ_ASSERT(mTransactionThread); - MOZ_ASSERT(PR_GetCurrentThread() == mTransactionThread); - } - - bool - IsActorDestroyed() const - { - AssertIsOnBackgroundThread(); - - return mActorDestroyed; - } - - // Must be called on the background thread. - bool - IsInvalidated() const - { - MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()"); - MOZ_ASSERT_IF(mInvalidated, NS_FAILED(mResultCode)); - - return mInvalidated; - } - - // May be called on any thread, but is more expensive than IsInvalidated(). - bool - IsInvalidatedOnAnyThread() const - { - return mInvalidatedOnAnyThread; - } - - void - SetActive() - { - AssertIsOnBackgroundThread(); - - mHasBeenActive = true; - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING( - mozilla::dom::indexedDB::TransactionBase) - - nsresult - GetCachedStatement(const nsACString& aQuery, - CachedStatement* aCachedStatement); - - template - nsresult - GetCachedStatement(const char (&aQuery)[N], - CachedStatement* aCachedStatement) - { - AssertIsOnTransactionThread(); - MOZ_ASSERT(aCachedStatement); - - return GetCachedStatement(NS_LITERAL_CSTRING(aQuery), aCachedStatement); - } - - nsresult - EnsureConnection(); - - void - Abort(nsresult aResultCode, bool aForce); - - mozIStorageConnection* - Connection() const - { - AssertIsOnTransactionThread(); - MOZ_ASSERT(mConnection); - - return mConnection; - } - - uint64_t - TransactionId() const - { - return mTransactionId; - } - - const nsCString& - DatabaseId() const - { - return mDatabaseId; - } - - Mode - GetMode() const - { - return mMode; - } - - Database* - GetDatabase() const - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mDatabase); - - return mDatabase; - } - - bool - IsAborted() const - { - AssertIsOnBackgroundThread(); - - return NS_FAILED(mResultCode); - } - - already_AddRefed - GetMetadataForObjectStoreId(int64_t aObjectStoreId) const; - - already_AddRefed - GetMetadataForIndexId(FullObjectStoreMetadata* const aObjectStoreMetadata, - int64_t aIndexId) const; - - PBackgroundParent* - GetBackgroundParent() const - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!IsActorDestroyed()); - - return GetDatabase()->GetBackgroundParent(); - } - - TransactionThreadPool* - GetTransactionThreadPool() const - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransactionThreadPool); - - return mTransactionThreadPool; - } - - void - NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata); - - void - ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata); - - nsresult - StartSavepoint(); - - nsresult - ReleaseSavepoint(); - - nsresult - RollbackSavepoint(); - - void - NoteActiveRequest(); - - void - NoteFinishedRequest(); - - void - Invalidate(); - -protected: - TransactionBase(Database* aDatabase, - Mode aMode); - - virtual - ~TransactionBase(); - - void - NoteActorDestroyed() - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; - } - -#ifdef DEBUG - // Only called by VersionChangeTransaction. - void - FakeActorDestroyed() - { - mActorDestroyed = true; - } -#endif - - bool - RecvCommit(); - - bool - RecvAbort(nsresult aResultCode); - - void - MaybeCommitOrAbort() - { - AssertIsOnBackgroundThread(); - - // If we've already committed or aborted then there's nothing else to do. - if (mCommittedOrAborted) { - return; - } - - // If there are active requests then we have to wait for those requests to - // complete (see NoteFinishedRequest). - if (mActiveRequestCount) { - return; - } - - // If we haven't yet received a commit or abort message then there could be - // additional requests coming so we should wait unless we're being forced to - // abort. - if (!mCommitOrAbortReceived && !mForceAborted) { - return; - } - - CommitOrAbort(); - } - - PBackgroundIDBRequestParent* - AllocRequest(const RequestParams& aParams, bool aTrustParams); - - bool - StartRequest(PBackgroundIDBRequestParent* aActor); - - bool - DeallocRequest(PBackgroundIDBRequestParent* aActor); - - PBackgroundIDBCursorParent* - AllocCursor(const OpenCursorParams& aParams, bool aTrustParams); - - bool - StartCursor(PBackgroundIDBCursorParent* aActor, - const OpenCursorParams& aParams); - - bool - DeallocCursor(PBackgroundIDBCursorParent* aActor); - - virtual void - UpdateMetadata(nsresult aResult) - { } - - virtual bool - SendCompleteNotification(nsresult aResult) = 0; - -private: - // Only called by CommitOp. - void - ReleaseTransactionThreadObjects(); - - // Only called by CommitOp. - void - ReleaseBackgroundThreadObjects(); - - bool - VerifyRequestParams(const RequestParams& aParams) const; - - bool - VerifyRequestParams(const OpenCursorParams& aParams) const; - - bool - VerifyRequestParams(const CursorRequestParams& aParams) const; - - bool - VerifyRequestParams(const SerializedKeyRange& aKeyRange) const; - - bool - VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const; - - bool - VerifyRequestParams(const OptionalKeyRange& aKeyRange) const; - - void - CommitOrAbort(); -}; - -class TransactionBase::CommitOp MOZ_FINAL - : public DatabaseOperationBase - , public TransactionThreadPool::FinishCallback -{ - friend class TransactionBase; - - nsRefPtr mTransaction; - nsresult mResultCode; - -private: - CommitOp(TransactionBase* aTransaction, - nsresult aResultCode) - : mTransaction(aTransaction) - , mResultCode(aResultCode) - { - MOZ_ASSERT(aTransaction); - } - - ~CommitOp() - { } - - // Writes new autoIncrement counts to database. - nsresult - WriteAutoIncrementCounts(); - - // Updates counts after a database activity has finished. - void - CommitOrRollbackAutoIncrementCounts(); - - NS_DECL_NSIRUNNABLE - - virtual void - TransactionFinishedBeforeUnblock() MOZ_OVERRIDE; - - virtual void - TransactionFinishedAfterUnblock() MOZ_OVERRIDE; - -public: - void - AssertIsOnTransactionThread() const - { - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnTransactionThread(); - } - - NS_DECL_ISUPPORTS_INHERITED -}; - -class TransactionBase::UpdateRefcountFunction MOZ_FINAL - : public mozIStorageFunction -{ - class FileInfoEntry - { - friend class UpdateRefcountFunction; - - nsRefPtr mFileInfo; - int32_t mDelta; - int32_t mSavepointDelta; - - public: - explicit FileInfoEntry(FileInfo* aFileInfo) - : mFileInfo(aFileInfo) - , mDelta(0) - , mSavepointDelta(0) - { } - }; - - enum UpdateType - { - eIncrement, - eDecrement - }; - - class DatabaseUpdateFunction - { - nsCOMPtr mConnection; - nsCOMPtr mUpdateStatement; - nsCOMPtr mSelectStatement; - nsCOMPtr mInsertStatement; - - UpdateRefcountFunction* mFunction; - - nsresult mErrorCode; - - public: - DatabaseUpdateFunction(mozIStorageConnection* aConnection, - UpdateRefcountFunction* aFunction) - : mConnection(aConnection) - , mFunction(aFunction) - , mErrorCode(NS_OK) - { } - - bool - Update(int64_t aId, int32_t aDelta); - - nsresult - ErrorCode() const - { - return mErrorCode; - } - - private: - nsresult - UpdateInternal(int64_t aId, int32_t aDelta); - }; - - FileManager* mFileManager; - nsClassHashtable mFileInfoEntries; - nsDataHashtable mSavepointEntriesIndex; - - nsTArray mJournalsToCreateBeforeCommit; - nsTArray mJournalsToRemoveAfterCommit; - nsTArray mJournalsToRemoveAfterAbort; - - bool mInSavepoint; - -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_MOZISTORAGEFUNCTION - - explicit UpdateRefcountFunction(FileManager* aFileManager) - : mFileManager(aFileManager) - , mInSavepoint(false) - { } - - void - ClearFileInfoEntries() - { - mFileInfoEntries.Clear(); - } - - nsresult - WillCommit(mozIStorageConnection* aConnection); - - void - DidCommit(); - - void - DidAbort(); - - void - StartSavepoint(); - - void - ReleaseSavepoint(); - - void - RollbackSavepoint(); - -private: - ~UpdateRefcountFunction() - { } - - nsresult - ProcessValue(mozIStorageValueArray* aValues, - int32_t aIndex, - UpdateType aUpdateType); - - nsresult - CreateJournals(); - - nsresult - RemoveJournals(const nsTArray& aJournals); - - static PLDHashOperator - DatabaseUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg); - - static PLDHashOperator - FileInfoUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg); -}; - -class MOZ_STACK_CLASS TransactionBase::AutoSavepoint MOZ_FINAL -{ - TransactionBase* mTransaction; - -public: - AutoSavepoint() - : mTransaction(nullptr) - { } - - ~AutoSavepoint(); - - nsresult - Start(TransactionBase* aTransaction); - - nsresult - Commit(); -}; - -class TransactionBase::CachedStatement MOZ_FINAL -{ - friend class TransactionBase; - - nsCOMPtr mStatement; - Maybe mScoper; - -public: - CachedStatement() - { } - - ~CachedStatement() - { } - - operator mozIStorageStatement*() - { - return mStatement; - } - - mozIStorageStatement* - operator->() - { - MOZ_ASSERT(mStatement); - return mStatement; - } - - void - Reset() - { - MOZ_ASSERT_IF(mStatement, mScoper); - - if (mStatement) { - mScoper.reset(); - mScoper.emplace(mStatement); - } - } - -private: - // Only called by TransactionBase. - void - Assign(already_AddRefed aStatement) - { - mScoper.reset(); - - mStatement = aStatement; - - if (mStatement) { - mScoper.emplace(mStatement); - } - } - - // No funny business allowed. - CachedStatement(const CachedStatement&) MOZ_DELETE; - CachedStatement& operator=(const CachedStatement&) MOZ_DELETE; -}; - -class NormalTransaction MOZ_FINAL - : public TransactionBase - , public PBackgroundIDBTransactionParent -{ - friend class Database; - - nsTArray> mObjectStores; - -private: - // This constructor is only called by Database. - NormalTransaction(Database* aDatabase, - nsTArray>& aObjectStores, - TransactionBase::Mode aMode); - - // Reference counted. - ~NormalTransaction() - { } - - bool - IsSameProcessActor(); - - // Only called by TransactionBase. - virtual bool - SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE; - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvDeleteMe() MOZ_OVERRIDE; - - virtual bool - RecvCommit() MOZ_OVERRIDE; - - virtual bool - RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE; - - virtual PBackgroundIDBRequestParent* - AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor, - const RequestParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBCursorParent* - AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor, - const OpenCursorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor) - MOZ_OVERRIDE; -}; - -class VersionChangeTransaction MOZ_FINAL - : public TransactionBase - , public PBackgroundIDBVersionChangeTransactionParent -{ - friend class OpenDatabaseOp; - - nsRefPtr mOpenDatabaseOp; - nsRefPtr mOldMetadata; - - bool mActorWasAlive; - -private: - // Only called by OpenDatabaseOp. - explicit VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp); - - // Reference counted. - ~VersionChangeTransaction(); - - bool - IsSameProcessActor(); - - // Only called by OpenDatabaseOp. - bool - CopyDatabaseMetadata(); - - void - SetActorAlive(); - - // Only called by TransactionBase. - virtual void - UpdateMetadata(nsresult aResult) MOZ_OVERRIDE; - - // Only called by TransactionBase. - virtual bool - SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE; - - // IPDL methods are only called by IPDL. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvDeleteMe() MOZ_OVERRIDE; - - virtual bool - RecvCommit() MOZ_OVERRIDE; - - virtual bool - RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE; - - virtual bool - RecvCreateObjectStore(const ObjectStoreMetadata& aMetadata) MOZ_OVERRIDE; - - virtual bool - RecvDeleteObjectStore(const int64_t& aObjectStoreId) MOZ_OVERRIDE; - - virtual bool - RecvCreateIndex(const int64_t& aObjectStoreId, - const IndexMetadata& aMetadata) MOZ_OVERRIDE; - - virtual bool - RecvDeleteIndex(const int64_t& aObjectStoreId, - const int64_t& aIndexId) MOZ_OVERRIDE; - - virtual PBackgroundIDBRequestParent* - AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor, - const RequestParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor) - MOZ_OVERRIDE; - - virtual PBackgroundIDBCursorParent* - AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE; - - virtual bool - RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor, - const OpenCursorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor) - MOZ_OVERRIDE; -}; - -class FactoryOp - : public DatabaseOperationBase - , public PBackgroundIDBFactoryRequestParent -{ -public: - struct MaybeBlockedDatabaseInfo; - -protected: - enum State - { - // Just created on the PBackground thread, dispatched to the main thread. - // Next step is State_OpenPending. - State_Initial, - - // Waiting for open allowed on the main thread. The next step is either - // State_SendingResults if permission is denied, - // State_PermissionChallenge if the permission is unknown, or - // State_DatabaseWorkOpen if permission is granted. - State_OpenPending, - - // Sending a permission challenge message to the child on the PBackground - // thread. Next step is State_PermissionRetryReady. - State_PermissionChallenge, - - // Retrying permission check after a challenge on the main thread. Next step - // is either State_SendingResults if permission is denied or - // State_DatabaseWorkOpen if permission is granted. - State_PermissionRetry, - - // Waiting to do/doing work on the QuotaManager IO thread. Its next step is - // either State_BeginVersionChange if the requested version doesn't match - // the existing database version or State_SendingResults if the versions - // match. - State_DatabaseWorkOpen, - - // Starting a version change transaction or deleting a database on the - // PBackground thread. We need to notify other databases that a version - // change is about to happen, and maybe tell the request that a version - // change has been blocked. If databases are notified then the next step is - // State_WaitingForOtherDatabasesToClose. Otherwise the next step is - // State_DispatchToWorkThread. - State_BeginVersionChange, - - // Waiting for other databases to close on the PBackground thread. This - // state may persist until all databases are closed. The next state is - // State_WaitingForTransactionsToComplete. - State_WaitingForOtherDatabasesToClose, - - // Waiting for all transactions that could interfere with this operation to - // complete on the PBackground thread. Next state is - // State_DatabaseWorkVersionChange. - State_WaitingForTransactionsToComplete, - - // Waiting to do/doing work on the "work thread". This involves waiting for - // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a - // different implementation) to do its work. Eventually the state will - // transition to State_SendingResults. - State_DatabaseWorkVersionChange, - - // Waiting to send/sending results on the PBackground thread. Next step is - // UnblockingQuotaManager. - State_SendingResults, - - // Notifying the QuotaManager that it can proceed to the next operation on - // the main thread. Next step is Completed. - State_UnblockingQuotaManager, - - // All done. - State_Completed - }; - - // Must be released on the background thread! - nsRefPtr mFactory; - - // Must be released on the main thread! - nsRefPtr mContentParent; - - nsTArray mMaybeBlockedDatabases; - - const CommonFactoryRequestParams mCommonParams; - nsCString mGroup; - nsCString mOrigin; - nsCString mDatabaseId; - State mState; - StoragePrivilege mStoragePrivilege; - const bool mDeleting; - bool mBlockedQuotaManager; - bool mChromeWriteAccessAllowed; - -public: - void - NoteDatabaseBlocked(Database* aDatabase); - - virtual void - NoteDatabaseClosed(Database* aDatabase) = 0; - -#ifdef DEBUG - bool - HasBlockedDatabases() const - { - return !mMaybeBlockedDatabases.IsEmpty(); - } -#endif - -protected: - FactoryOp(Factory* aFactory, - already_AddRefed aContentParent, - const CommonFactoryRequestParams& aCommonParams, - bool aDeleting) - : mFactory(aFactory) - , mContentParent(Move(aContentParent)) - , mCommonParams(aCommonParams) - , mState(State_Initial) - , mStoragePrivilege(mozilla::dom::quota::Content) - , mDeleting(aDeleting) - , mBlockedQuotaManager(false) - , mChromeWriteAccessAllowed(false) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aFactory); - } - - virtual - ~FactoryOp() - { - // Normally this would be out-of-line since it is a virtual function but - // MSVC 2010 fails to link for some reason if it is not inlined here... - MOZ_ASSERT_IF(OperationMayProceed(), - mState == State_Initial || mState == State_Completed); - } - - nsresult - Open(); - - nsresult - ChallengePermission(); - - nsresult - RetryCheckPermission(); - - nsresult - SendToIOThread(); - - void - WaitForTransactions(); - - void - FinishSendResults(); - - void - UnblockQuotaManager(); - - nsresult - SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo, - Database* aOpeningDatabase, - uint64_t aOldVersion, - const NullableVersion& aNewVersion); - - // Methods that subclasses must implement. - virtual nsresult - QuotaManagerOpen() = 0; - - virtual nsresult - DoDatabaseWork() = 0; - - virtual nsresult - BeginVersionChange() = 0; - - virtual nsresult - DispatchToWorkThread() = 0; - - virtual void - SendResults() = 0; - - // Common nsIRunnable implementation that subclasses may not override. - NS_IMETHOD - Run() MOZ_FINAL; - - // IPDL methods. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvPermissionRetry() MOZ_OVERRIDE; - - virtual void - SendBlockedNotification() = 0; - -private: - nsresult - CheckPermission(ContentParent* aContentParent, - PermissionRequestBase::PermissionValue* aPermission); - - static bool - CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, - const nsACString& aPermissionString); - - nsresult - FinishOpen(); -}; - -struct FactoryOp::MaybeBlockedDatabaseInfo MOZ_FINAL -{ - nsRefPtr mDatabase; - bool mBlocked; - - MOZ_IMPLICIT MaybeBlockedDatabaseInfo(Database* aDatabase) - : mDatabase(aDatabase) - , mBlocked(false) - { - MOZ_ASSERT(aDatabase); - - MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo); - } - - ~MaybeBlockedDatabaseInfo() - { - MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo); - } - - bool - operator==(const MaybeBlockedDatabaseInfo& aOther) const - { - return mDatabase == aOther.mDatabase; - } - - bool - operator<(const MaybeBlockedDatabaseInfo& aOther) const - { - return mDatabase < aOther.mDatabase; - } - - Database* - operator->() - { - return mDatabase; - } -}; - -class OpenDatabaseOp MOZ_FINAL - : public FactoryOp -{ - friend class Database; - friend class VersionChangeTransaction; - - class VersionChangeOp; - - const OptionalWindowId mOptionalWindowId; - const OptionalWindowId mOptionalContentParentId; - - nsRefPtr mMetadata; - - uint64_t mRequestedVersion; - nsString mDatabaseFilePath; - nsRefPtr mFileManager; - - nsRefPtr mDatabase; - nsRefPtr mVersionChangeTransaction; - - nsRefPtr mOfflineStorage; - -public: - OpenDatabaseOp(Factory* aFactory, - already_AddRefed aContentParent, - const OptionalWindowId& aOptionalWindowId, - const CommonFactoryRequestParams& aParams); - - bool - IsOtherProcessActor() const - { - MOZ_ASSERT(mOptionalContentParentId.type() != OptionalWindowId::T__None); - - return mOptionalContentParentId.type() == OptionalWindowId::Tuint64_t; - } - -private: - ~OpenDatabaseOp() - { } - - nsresult - LoadDatabaseInformation(mozIStorageConnection* aConnection); - - nsresult - SendUpgradeNeeded(); - - void - EnsureDatabaseActor(); - - nsresult - EnsureDatabaseActorIsAlive(); - - void - MetadataToSpec(DatabaseSpec& aSpec); - - void - AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata) -#ifdef DEBUG - ; -#else - { } -#endif - - virtual nsresult - QuotaManagerOpen() MOZ_OVERRIDE; - - virtual nsresult - DoDatabaseWork() MOZ_OVERRIDE; - - virtual nsresult - BeginVersionChange() MOZ_OVERRIDE; - - virtual void - NoteDatabaseClosed(Database* aDatabase) MOZ_OVERRIDE; - - virtual void - SendBlockedNotification() MOZ_OVERRIDE; - - virtual nsresult - DispatchToWorkThread() MOZ_OVERRIDE; - - virtual void - SendResults() MOZ_OVERRIDE; -}; - -class OpenDatabaseOp::VersionChangeOp MOZ_FINAL - : public TransactionDatabaseOperationBase -{ - friend class OpenDatabaseOp; - - nsRefPtr mOpenDatabaseOp; - const uint64_t mRequestedVersion; - uint64_t mPreviousVersion; - -private: - explicit VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp) - : TransactionDatabaseOperationBase( - aOpenDatabaseOp->mVersionChangeTransaction) - , mOpenDatabaseOp(aOpenDatabaseOp) - , mRequestedVersion(aOpenDatabaseOp->mRequestedVersion) - , mPreviousVersion(aOpenDatabaseOp->mMetadata->mCommonMetadata.version()) - { - MOZ_ASSERT(aOpenDatabaseOp); - MOZ_ASSERT(mRequestedVersion); - } - - ~VersionChangeOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual nsresult - SendSuccessResult() MOZ_OVERRIDE; - - virtual bool - SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; - - virtual void - Cleanup() MOZ_OVERRIDE; -}; - -class DeleteDatabaseOp MOZ_FINAL - : public FactoryOp -{ - class VersionChangeOp; - - nsString mDatabaseDirectoryPath; - nsString mDatabaseFilenameBase; - uint64_t mPreviousVersion; - -public: - DeleteDatabaseOp(Factory* aFactory, - already_AddRefed aContentParent, - const CommonFactoryRequestParams& aParams) - : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ true) - , mPreviousVersion(0) - { } - -private: - ~DeleteDatabaseOp() - { } - - void - LoadPreviousVersion(nsIFile* aDatabaseFile); - - virtual nsresult - QuotaManagerOpen() MOZ_OVERRIDE; - - virtual nsresult - DoDatabaseWork() MOZ_OVERRIDE; - - virtual nsresult - BeginVersionChange() MOZ_OVERRIDE; - - virtual void - NoteDatabaseClosed(Database* aDatabase) MOZ_OVERRIDE; - - virtual void - SendBlockedNotification() MOZ_OVERRIDE; - - virtual nsresult - DispatchToWorkThread() MOZ_OVERRIDE; - - virtual void - SendResults() MOZ_OVERRIDE; -}; - -class DeleteDatabaseOp::VersionChangeOp MOZ_FINAL - : public DatabaseOperationBase -{ - friend class DeleteDatabaseOp; - - nsRefPtr mDeleteDatabaseOp; - -private: - explicit VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp) - : mDeleteDatabaseOp(aDeleteDatabaseOp) - { - MOZ_ASSERT(aDeleteDatabaseOp); - MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty()); - } - - ~VersionChangeOp() - { } - - // XXX This should be much simpler when the QuotaManager lives on the - // PBackground thread. - nsresult - RunOnMainThread(); - - nsresult - RunOnIOThread(); - - void - RunOnOwningThread(); - - NS_DECL_NSIRUNNABLE -}; - -class VersionChangeTransactionOp - : public TransactionDatabaseOperationBase -{ -public: - virtual void - Cleanup() MOZ_OVERRIDE; - -protected: - explicit VersionChangeTransactionOp(VersionChangeTransaction* aTransaction) - : TransactionDatabaseOperationBase(aTransaction) - { } - - virtual - ~VersionChangeTransactionOp() - { } - -private: - virtual nsresult - SendSuccessResult() MOZ_OVERRIDE; - - virtual bool - SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; -}; - -class CreateObjectStoreOp MOZ_FINAL - : public VersionChangeTransactionOp -{ - friend class VersionChangeTransaction; - - const ObjectStoreMetadata mMetadata; - -private: - // Only created by VersionChangeTransaction. - CreateObjectStoreOp(VersionChangeTransaction* aTransaction, - const ObjectStoreMetadata& aMetadata) - : VersionChangeTransactionOp(aTransaction) - , mMetadata(aMetadata) - { - MOZ_ASSERT(aMetadata.id()); - } - - ~CreateObjectStoreOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; -}; - -class DeleteObjectStoreOp MOZ_FINAL - : public VersionChangeTransactionOp -{ - friend class VersionChangeTransaction; - - const nsRefPtr mMetadata; - -private: - // Only created by VersionChangeTransaction. - DeleteObjectStoreOp(VersionChangeTransaction* aTransaction, - FullObjectStoreMetadata* const aMetadata) - : VersionChangeTransactionOp(aTransaction) - , mMetadata(aMetadata) - { - MOZ_ASSERT(aMetadata->mCommonMetadata.id()); - } - - ~DeleteObjectStoreOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; -}; - -class CreateIndexOp MOZ_FINAL - : public VersionChangeTransactionOp -{ - friend class VersionChangeTransaction; - - class ThreadLocalJSRuntime; - friend class ThreadLocalJSRuntime; - - static const unsigned int kBadThreadLocalIndex = - static_cast(-1); - - static unsigned int sThreadLocalIndex; - - const IndexMetadata mMetadata; - Maybe mMaybeUniqueIndexTable; - nsRefPtr mFileManager; - const nsCString mDatabaseId; - const uint64_t mObjectStoreId; - -private: - // Only created by VersionChangeTransaction. - CreateIndexOp(VersionChangeTransaction* aTransaction, - const int64_t aObjectStoreId, - const IndexMetadata& aMetadata); - - ~CreateIndexOp() - { } - - static void - InitThreadLocals(); - - nsresult - InsertDataFromObjectStore(TransactionBase* aTransaction); - - virtual bool - Init(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; -}; - -class CreateIndexOp::ThreadLocalJSRuntime MOZ_FINAL -{ - friend class CreateIndexOp; - friend class nsAutoPtr; - - static const JSClass kGlobalClass; - static const uint32_t kRuntimeHeapSize = 768 * 1024; - - JSRuntime* mRuntime; - JSContext* mContext; - JSObject* mGlobal; - -public: - static ThreadLocalJSRuntime* - GetOrCreate(); - - JSContext* - Context() const - { - return mContext; - } - - JSObject* - Global() const - { - return mGlobal; - } - -private: - ThreadLocalJSRuntime() - : mRuntime(nullptr) - , mContext(nullptr) - , mGlobal(nullptr) - { - MOZ_COUNT_CTOR(CreateIndexOp::ThreadLocalJSRuntime); - } - - ~ThreadLocalJSRuntime() - { - MOZ_COUNT_DTOR(CreateIndexOp::ThreadLocalJSRuntime); - - if (mContext) { - JS_DestroyContext(mContext); - } - - if (mRuntime) { - JS_DestroyRuntime(mRuntime); - } - } - - bool - Init(); -}; - -class DeleteIndexOp MOZ_FINAL - : public VersionChangeTransactionOp -{ - friend class VersionChangeTransaction; - - const int64_t mIndexId; - -private: - // Only created by VersionChangeTransaction. - DeleteIndexOp(VersionChangeTransaction* aTransaction, - const int64_t aIndexId) - : VersionChangeTransactionOp(aTransaction) - , mIndexId(aIndexId) - { - MOZ_ASSERT(aIndexId); - } - - ~DeleteIndexOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; -}; - -class NormalTransactionOp - : public TransactionDatabaseOperationBase - , public PBackgroundIDBRequestParent -{ - DebugOnly mResponseSent; - -public: - virtual void - Cleanup() MOZ_OVERRIDE; - -protected: - explicit NormalTransactionOp(TransactionBase* aTransaction) - : TransactionDatabaseOperationBase(aTransaction) - , mResponseSent(false) - { } - - virtual - ~NormalTransactionOp() - { } - - // Subclasses use this override to set the IPDL response value. - virtual void - GetResponse(RequestResponse& aResponse) = 0; - -private: - virtual nsresult - SendSuccessResult() MOZ_OVERRIDE; - - virtual bool - SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; - - // IPDL methods. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; -}; - -class ObjectStoreAddOrPutRequestOp MOZ_FINAL - : public NormalTransactionOp -{ - friend class TransactionBase; - - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - struct StoredFileInfo; - - const ObjectStoreAddPutParams mParams; - Maybe mUniqueIndexTable; - - // This must be non-const so that we can update the mNextAutoIncrementId field - // if we are modifying an autoIncrement objectStore. - nsRefPtr mMetadata; - - FallibleTArray mStoredFileInfos; - - nsRefPtr mFileManager; - - Key mResponse; - const nsCString mGroup; - const nsCString mOrigin; - const PersistenceType mPersistenceType; - const bool mOverwrite; - -private: - // Only created by TransactionBase. - ObjectStoreAddOrPutRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams); - - ~ObjectStoreAddOrPutRequestOp() - { } - - nsresult - CopyFileData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream); - - virtual bool - Init(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; - - virtual void - Cleanup() MOZ_OVERRIDE; -}; - -struct ObjectStoreAddOrPutRequestOp::StoredFileInfo MOZ_FINAL -{ - nsRefPtr mFileActor; - nsRefPtr mFileInfo; - nsCOMPtr mInputStream; - bool mCopiedSuccessfully; - - StoredFileInfo() - : mCopiedSuccessfully(false) - { - AssertIsOnBackgroundThread(); - - MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); - } - - ~StoredFileInfo() - { - AssertIsOnBackgroundThread(); - - MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); - } -}; - -class ObjectStoreGetRequestOp MOZ_FINAL - : public NormalTransactionOp -{ - friend class TransactionBase; - - const uint32_t mObjectStoreId; - nsRefPtr mFileManager; - const OptionalKeyRange mOptionalKeyRange; - AutoFallibleTArray mResponse; - PBackgroundParent* mBackgroundParent; - const uint32_t mLimit; - const bool mGetAll; - -private: - // Only created by TransactionBase. - ObjectStoreGetRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams, - bool aGetAll); - - ~ObjectStoreGetRequestOp() - { } - - nsresult - ConvertResponse(uint32_t aIndex, - SerializedStructuredCloneReadInfo& aSerializedInfo); - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; -}; - -class ObjectStoreGetAllKeysRequestOp MOZ_FINAL - : public NormalTransactionOp -{ - friend class TransactionBase; - - const ObjectStoreGetAllKeysParams mParams; - FallibleTArray mResponse; - -private: - // Only created by TransactionBase. - ObjectStoreGetAllKeysRequestOp(TransactionBase* aTransaction, - const ObjectStoreGetAllKeysParams& aParams) - : NormalTransactionOp(aTransaction) - , mParams(aParams) - { } - - ~ObjectStoreGetAllKeysRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; -}; - -class ObjectStoreDeleteRequestOp MOZ_FINAL - : public NormalTransactionOp -{ - friend class TransactionBase; - - const ObjectStoreDeleteParams mParams; - ObjectStoreDeleteResponse mResponse; - -private: - ObjectStoreDeleteRequestOp(TransactionBase* aTransaction, - const ObjectStoreDeleteParams& aParams) - : NormalTransactionOp(aTransaction) - , mParams(aParams) - { } - - ~ObjectStoreDeleteRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE - { - aResponse = Move(mResponse); - } -}; - -class ObjectStoreClearRequestOp MOZ_FINAL - : public NormalTransactionOp -{ - friend class TransactionBase; - - const ObjectStoreClearParams mParams; - ObjectStoreClearResponse mResponse; - -private: - ObjectStoreClearRequestOp(TransactionBase* aTransaction, - const ObjectStoreClearParams& aParams) - : NormalTransactionOp(aTransaction) - , mParams(aParams) - { } - - ~ObjectStoreClearRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE - { - aResponse = Move(mResponse); - } -}; - -class ObjectStoreCountRequestOp MOZ_FINAL - : public NormalTransactionOp -{ - friend class TransactionBase; - - const ObjectStoreCountParams mParams; - ObjectStoreCountResponse mResponse; - -private: - ObjectStoreCountRequestOp(TransactionBase* aTransaction, - const ObjectStoreCountParams& aParams) - : NormalTransactionOp(aTransaction) - , mParams(aParams) - { } - - ~ObjectStoreCountRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE - { - aResponse = Move(mResponse); - } -}; - -class IndexRequestOpBase - : public NormalTransactionOp -{ -protected: - const nsRefPtr mMetadata; - -protected: - IndexRequestOpBase(TransactionBase* aTransaction, - const RequestParams& aParams) - : NormalTransactionOp(aTransaction) - , mMetadata(IndexMetadataForParams(aTransaction, aParams)) - { } - - virtual - ~IndexRequestOpBase() - { } - -private: - static already_AddRefed - IndexMetadataForParams(TransactionBase* aTransaction, - const RequestParams& aParams); -}; - -class IndexGetRequestOp MOZ_FINAL - : public IndexRequestOpBase -{ - friend class TransactionBase; - - nsRefPtr mFileManager; - const OptionalKeyRange mOptionalKeyRange; - AutoFallibleTArray mResponse; - PBackgroundParent* mBackgroundParent; - const uint32_t mLimit; - const bool mGetAll; - -private: - // Only created by TransactionBase. - IndexGetRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams, - bool aGetAll); - - ~IndexGetRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; -}; - -class IndexGetKeyRequestOp MOZ_FINAL - : public IndexRequestOpBase -{ - friend class TransactionBase; - - const OptionalKeyRange mOptionalKeyRange; - AutoFallibleTArray mResponse; - const uint32_t mLimit; - const bool mGetAll; - -private: - // Only created by TransactionBase. - IndexGetKeyRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams, - bool aGetAll); - - ~IndexGetKeyRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; -}; - -class IndexCountRequestOp MOZ_FINAL - : public IndexRequestOpBase -{ - friend class TransactionBase; - - const IndexCountParams mParams; - IndexCountResponse mResponse; - -private: - // Only created by TransactionBase. - IndexCountRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams) - : IndexRequestOpBase(aTransaction, aParams) - , mParams(aParams.get_IndexCountParams()) - { } - - ~IndexCountRequestOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual void - GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE - { - aResponse = Move(mResponse); - } -}; - -class Cursor MOZ_FINAL : - public PBackgroundIDBCursorParent -{ - friend class TransactionBase; - - class ContinueOp; - class CursorOpBase; - class OpenOp; - -public: - typedef OpenCursorParams::Type Type; - -private: - nsRefPtr mTransaction; - nsRefPtr mFileManager; - PBackgroundParent* mBackgroundParent; - - const int64_t mObjectStoreId; - const int64_t mIndexId; - - nsCString mContinueQuery; - nsCString mContinueToQuery; - - Key mKey; - Key mObjectKey; - Key mRangeKey; - - CursorOpBase* mCurrentlyRunningOp; - - const Type mType; - const Direction mDirection; - - bool mUniqueIndex; - bool mActorDestroyed; - -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Cursor) - -private: - // Only created by TransactionBase. - Cursor(TransactionBase* aTransaction, - Type aType, - int64_t aObjectStoreId, - int64_t aIndexId, - Direction aDirection); - - // Reference counted. - ~Cursor() - { - MOZ_ASSERT(mActorDestroyed); - } - - // Only called by TransactionBase. - bool - Start(const OpenCursorParams& aParams); - - void - SendResponseInternal(CursorResponse& aResponse, - const nsTArray& aFiles); - - // Must call SendResponseInternal! - bool - SendResponse(const CursorResponse& aResponse) MOZ_DELETE; - - // IPDL methods. - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvDeleteMe() MOZ_OVERRIDE; - - virtual bool - RecvContinue(const CursorRequestParams& aParams) MOZ_OVERRIDE; -}; - -class Cursor::CursorOpBase - : public TransactionDatabaseOperationBase -{ -protected: - nsRefPtr mCursor; - FallibleTArray mFiles; - - CursorResponse mResponse; - - DebugOnly mResponseSent; - -protected: - explicit CursorOpBase(Cursor* aCursor) - : TransactionDatabaseOperationBase(aCursor->mTransaction) - , mCursor(aCursor) - , mResponseSent(false) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aCursor); - } - - virtual - ~CursorOpBase() - { } - - virtual bool - SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; - - virtual void - Cleanup() MOZ_OVERRIDE; -}; - -class Cursor::OpenOp MOZ_FINAL - : public Cursor::CursorOpBase -{ - friend class Cursor; - - const OptionalKeyRange mOptionalKeyRange; - -private: - // Only created by Cursor. - OpenOp(Cursor* aCursor, - const OptionalKeyRange& aOptionalKeyRange) - : CursorOpBase(aCursor) - , mOptionalKeyRange(aOptionalKeyRange) - { } - - // Reference counted. - ~OpenOp() - { } - - void - GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen); - - nsresult - DoObjectStoreDatabaseWork(TransactionBase* aTransaction); - - nsresult - DoObjectStoreKeyDatabaseWork(TransactionBase* aTransaction); - - nsresult - DoIndexDatabaseWork(TransactionBase* aTransaction); - - nsresult - DoIndexKeyDatabaseWork(TransactionBase* aTransaction); - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual nsresult - SendSuccessResult() MOZ_OVERRIDE; -}; - -class Cursor::ContinueOp MOZ_FINAL - : public Cursor::CursorOpBase -{ - friend class Cursor; - - const CursorRequestParams mParams; - -private: - // Only created by Cursor. - ContinueOp(Cursor* aCursor, const CursorRequestParams& aParams) - : CursorOpBase(aCursor) - , mParams(aParams) - { - MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None); - } - - // Reference counted. - ~ContinueOp() - { } - - virtual nsresult - DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; - - virtual nsresult - SendSuccessResult() MOZ_OVERRIDE; -}; - -class PermissionRequestHelper MOZ_FINAL - : public PermissionRequestBase - , public PIndexedDBPermissionRequestParent -{ - bool mActorDestroyed; - -public: - PermissionRequestHelper(nsPIDOMWindow* aWindow, - nsIPrincipal* aPrincipal) - : PermissionRequestBase(aWindow, aPrincipal) - , mActorDestroyed(false) - { } - -protected: - ~PermissionRequestHelper() - { } - -private: - virtual void - OnPromptComplete(PermissionValue aPermissionValue) MOZ_OVERRIDE; - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * Other class declarations - ******************************************************************************/ - -struct DatabaseActorInfo -{ - friend class nsAutoPtr; - - nsRefPtr mMetadata; - nsTArray mLiveDatabases; - nsRefPtr mWaitingFactoryOp; - - DatabaseActorInfo(FullDatabaseMetadata* aMetadata, - Database* aDatabase) - : mMetadata(aMetadata) - { - MOZ_ASSERT(aDatabase); - - MOZ_COUNT_CTOR(DatabaseActorInfo); - - mLiveDatabases.AppendElement(aDatabase); - } - -private: - ~DatabaseActorInfo() - { - MOZ_ASSERT(mLiveDatabases.IsEmpty()); - MOZ_ASSERT(!mWaitingFactoryOp || - !mWaitingFactoryOp->HasBlockedDatabases()); - - MOZ_COUNT_DTOR(DatabaseActorInfo); - } -}; - -class NonMainThreadHackBlob MOZ_FINAL - : public DOMFileImplFile -{ -public: - NonMainThreadHackBlob(nsIFile* aFile, FileInfo* aFileInfo) - : DOMFileImplFile(aFile, aFileInfo) - { - // Getting the content type is not currently supported off the main thread. - // This isn't a problem here because: - // - // 1. The real content type is stored in the structured clone data and - // that's all that the DOM will see. This blob's data will be updated - // during RecvSetMysteryBlobInfo(). - // 2. The nsExternalHelperAppService guesses the content type based only - // on the file extension. Our stored files have no extension so the - // current code path fails and sets the content type to the empty - // string. - // - // So, this is a hack to keep the nsExternalHelperAppService out of the - // picture entirely. Eventually we should probably fix this some other way. - mContentType.Truncate(); - } -}; - -class QuotaClient MOZ_FINAL - : public mozilla::dom::quota::Client -{ - class ShutdownTransactionThreadPoolRunnable; - friend class ShutdownTransactionThreadPoolRunnable; - - class WaitForTransactionsRunnable; - friend class WaitForTransactionsRunnable; - - static QuotaClient* sInstance; - - nsCOMPtr mBackgroundThread; - nsRefPtr mShutdownRunnable; - - // This is only touched on the background thread! - nsTArray> mDyingTransactionThreadPools; - - bool mShutdownRequested; - -public: - QuotaClient(); - - static QuotaClient* - GetInstance() - { - return sInstance; - } - - void - NoteBackgroundThread(nsIEventTarget* aBackgroundThread); - - void - NoteNewTransactionThreadPool(); - - void - NoteDyingTransactionThreadPool(); - - bool - HasShutDown() const - { - MOZ_ASSERT(NS_IsMainThread()); - - return mShutdownRequested; - } - - NS_INLINE_DECL_REFCOUNTING(QuotaClient) - - virtual mozilla::dom::quota::Client::Type - GetType() MOZ_OVERRIDE; - - virtual nsresult - InitOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - UsageInfo* aUsageInfo) MOZ_OVERRIDE; - - virtual nsresult - GetUsageForOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - UsageInfo* aUsageInfo) MOZ_OVERRIDE; - - virtual void - OnOriginClearCompleted(PersistenceType aPersistenceType, - const OriginOrPatternString& aOriginOrPattern) - MOZ_OVERRIDE; - - virtual void - ReleaseIOThreadObjects() MOZ_OVERRIDE; - - virtual bool - IsFileServiceUtilized() MOZ_OVERRIDE; - - virtual bool - IsTransactionServiceActivated() MOZ_OVERRIDE; - - virtual void - WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) MOZ_OVERRIDE; - - virtual void - AbortTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; - - virtual bool - HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; - - virtual void - ShutdownTransactionService() MOZ_OVERRIDE; - -private: - ~QuotaClient(); - - nsresult - GetDirectory(PersistenceType aPersistenceType, - const nsACString& aOrigin, - nsIFile** aDirectory); - - nsresult - GetUsageForDirectoryInternal(nsIFile* aDirectory, - UsageInfo* aUsageInfo, - bool aDatabaseFiles); -}; - -class QuotaClient::ShutdownTransactionThreadPoolRunnable MOZ_FINAL - : public nsRunnable -{ - nsRefPtr mQuotaClient; - bool mHasRequestedShutDown; - -public: - - explicit ShutdownTransactionThreadPoolRunnable(QuotaClient* aQuotaClient) - : mQuotaClient(aQuotaClient) - , mHasRequestedShutDown(false) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aQuotaClient); - MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); - MOZ_ASSERT(aQuotaClient->mShutdownRequested); - } - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~ShutdownTransactionThreadPoolRunnable() - { - MOZ_ASSERT(!mQuotaClient); - } - - NS_DECL_NSIRUNNABLE -}; - -class QuotaClient::WaitForTransactionsRunnable MOZ_FINAL - : public nsRunnable -{ - nsRefPtr mQuotaClient; - nsTArray mDatabaseIds; - nsCOMPtr mCallback; - - enum - { - State_Initial = 0, - State_WaitingForTransactions, - State_CallingCallback, - State_Complete - } mState; - -public: - WaitForTransactionsRunnable(QuotaClient* aQuotaClient, - nsTArray& aDatabaseIds, - nsIRunnable* aCallback) - : mQuotaClient(aQuotaClient) - , mCallback(aCallback) - , mState(State_Initial) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aQuotaClient); - MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); - MOZ_ASSERT(!aDatabaseIds.IsEmpty()); - MOZ_ASSERT(aCallback); - - mDatabaseIds.SwapElements(aDatabaseIds); - } - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~WaitForTransactionsRunnable() - { - MOZ_ASSERT(!mQuotaClient); - MOZ_ASSERT(!mCallback); - MOZ_ASSERT(mState = State_Complete); - } - - void - MaybeWait(); - - void - SendToMainThread(); - - void - CallCallback(); - - NS_DECL_NSIRUNNABLE -}; - -class DatabaseOfflineStorage MOZ_FINAL - : public nsIOfflineStorage -{ - // Must be released on the main thread! - nsRefPtr mStrongQuotaClient; - - // Only used on the main thread. - QuotaClient* mWeakQuotaClient; - - // Only used on the background thread. - Database* mDatabase; - - const OptionalWindowId mOptionalWindowId; - const OptionalWindowId mOptionalContentParentId; - const nsCString mOrigin; - const nsCString mId; - nsCOMPtr mOwningThread; - Atomic mTransactionCount; - - bool mClosedOnMainThread; - bool mClosedOnOwningThread; - bool mInvalidatedOnMainThread; - bool mInvalidatedOnOwningThread; - - DebugOnly mRegisteredWithQuotaManager; - -public: - DatabaseOfflineStorage(QuotaClient* aQuotaClient, - const OptionalWindowId& aOptionalWindowId, - const OptionalWindowId& aOptionalContentParentId, - const nsACString& aGroup, - const nsACString& aOrigin, - const nsACString& aId, - PersistenceType aPersistenceType, - nsIEventTarget* aOwningThread); - - static void - UnregisterOnOwningThread( - already_AddRefed aOfflineStorage); - - void - SetDatabase(Database* aDatabase) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aDatabase); - MOZ_ASSERT(!mDatabase); - - mDatabase = aDatabase; - } - - void - NoteNewTransaction() - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransactionCount < UINT32_MAX); - - mTransactionCount++; - } - - void - NoteFinishedTransaction() - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransactionCount); - - mTransactionCount--; - } - - bool - HasOpenTransactions() const - { - MOZ_ASSERT(NS_IsMainThread()); - - // XXX This is racy, is this correct? - return !!mTransactionCount; - } - - nsIEventTarget* - OwningThread() const - { - return mOwningThread; - } - - void - NoteRegisteredWithQuotaManager() - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mRegisteredWithQuotaManager); - - mRegisteredWithQuotaManager = true; - } - - void - CloseOnOwningThread(); - - NS_DECL_THREADSAFE_ISUPPORTS - -private: - ~DatabaseOfflineStorage() - { - MOZ_ASSERT(!mDatabase); - MOZ_ASSERT(!mRegisteredWithQuotaManager); - } - - void - CloseOnMainThread(); - - void - InvalidateOnMainThread(); - - void - InvalidateOnOwningThread(); - - void - UnregisterOnMainThread(); - - NS_DECL_NSIOFFLINESTORAGE -}; - -#ifdef DEBUG - -class DEBUGThreadSlower MOZ_FINAL - : public nsIThreadObserver -{ -public: - DEBUGThreadSlower() - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(kDEBUGThreadSleepMS); - } - - NS_DECL_ISUPPORTS - -private: - ~DEBUGThreadSlower() - { - AssertIsOnBackgroundThread(); - } - - NS_DECL_NSITHREADOBSERVER -}; - -#endif // DEBUG - -/******************************************************************************* - * Helper Functions - ******************************************************************************/ - -bool -TokenizerIgnoreNothing(char16_t /* aChar */) -{ - return false; -} - -nsresult -ConvertFileIdsToArray(const nsAString& aFileIds, - nsTArray& aResult) -{ - nsCharSeparatedTokenizerTemplate - tokenizer(aFileIds, ' '); - - nsAutoString token; - nsresult rv; - - while (tokenizer.hasMoreTokens()) { - token = tokenizer.nextToken(); - MOZ_ASSERT(!token.IsEmpty()); - - int32_t id = token.ToInteger(&rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - aResult.AppendElement(id); - } - - return NS_OK; -} - -bool -GetDatabaseBaseFilename(const nsAString& aFilename, - nsAString& aDatabaseBaseFilename) -{ - MOZ_ASSERT(!aFilename.IsEmpty()); - - NS_NAMED_LITERAL_STRING(sqlite, ".sqlite"); - - if (!StringEndsWith(aFilename, sqlite)) { - return false; - } - - aDatabaseBaseFilename = - Substring(aFilename, 0, aFilename.Length() - sqlite.Length()); - - return true; -} - -nsresult -ConvertBlobsToActors(PBackgroundParent* aBackgroundActor, - FileManager* aFileManager, - const nsTArray& aFiles, - FallibleTArray& aActors, - FallibleTArray& aFileInfos) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(aFileManager); - MOZ_ASSERT(aActors.IsEmpty()); - MOZ_ASSERT(aFileInfos.IsEmpty()); - - if (aFiles.IsEmpty()) { - return NS_OK; - } - - nsCOMPtr directory = aFileManager->GetDirectory(); - if (NS_WARN_IF(!directory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - DebugOnly exists; - MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists))); - MOZ_ASSERT(exists); - - DebugOnly isDirectory; - MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory))); - MOZ_ASSERT(isDirectory); - - const uint32_t count = aFiles.Length(); - - if (NS_WARN_IF(!aActors.SetCapacity(count))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - const bool collectFileInfos = - !BackgroundParent::IsOtherProcessActor(aBackgroundActor); - - if (collectFileInfos && NS_WARN_IF(!aFileInfos.SetCapacity(count))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - for (uint32_t index = 0; index < count; index++) { - const StructuredCloneFile& file = aFiles[index]; - - const int64_t fileId = file.mFileInfo->Id(); - MOZ_ASSERT(fileId > 0); - - nsCOMPtr nativeFile = - aFileManager->GetFileForId(directory, fileId); - if (NS_WARN_IF(!nativeFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - MOZ_ASSERT(NS_SUCCEEDED(nativeFile->Exists(&exists))); - MOZ_ASSERT(exists); - - DebugOnly isFile; - MOZ_ASSERT(NS_SUCCEEDED(nativeFile->IsFile(&isFile))); - MOZ_ASSERT(isFile); - - nsRefPtr impl = - new NonMainThreadHackBlob(nativeFile, file.mFileInfo); - - PBlobParent* actor = - BackgroundParent::GetOrCreateActorForBlobImpl(aBackgroundActor, impl); - if (!actor) { - // This can only fail if the child has crashed. - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - MOZ_ALWAYS_TRUE(aActors.AppendElement(actor)); - - if (collectFileInfos) { - nsRefPtr fileInfo = file.mFileInfo; - - // Transfer a reference to the receiver. - auto transferedFileInfo = - reinterpret_cast(fileInfo.forget().take()); - MOZ_ALWAYS_TRUE(aFileInfos.AppendElement(transferedFileInfo)); - } - } - - return NS_OK; -} - -/******************************************************************************* - * Globals - ******************************************************************************/ - -// Maps a database id to information about live database actors. -typedef nsClassHashtable - DatabaseActorHashtable; - -StaticAutoPtr gLiveDatabaseHashtable; - -StaticRefPtr gStartTransactionRunnable; - -TransactionThreadPool* gCurrentTransactionThreadPool = nullptr; - -#ifdef DEBUG - -StaticRefPtr gDEBUGThreadSlower; - -#endif // DEBUG - -} // anonymous namespace - -/******************************************************************************* - * Exported functions - ******************************************************************************/ - -PBackgroundIDBFactoryParent* -AllocPBackgroundIDBFactoryParent(PBackgroundParent* aManager, - const OptionalWindowId& aOptionalWindowId) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aOptionalWindowId.type() != OptionalWindowId::T__None); - - if (BackgroundParent::IsOtherProcessActor(aManager)) { - if (NS_WARN_IF(aOptionalWindowId.type() != OptionalWindowId::Tvoid_t)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - } - - nsRefPtr actor = Factory::Create(aOptionalWindowId); - return actor.forget().take(); -} - -bool -RecvPBackgroundIDBFactoryConstructor(PBackgroundParent* /* aManager */, - PBackgroundIDBFactoryParent* aActor, - const OptionalWindowId& aOptionalWindowId) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - return true; -} - -bool -DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - nsRefPtr actor = dont_AddRef(static_cast(aActor)); - return true; -} - -PIndexedDBPermissionRequestParent* -AllocPIndexedDBPermissionRequestParent(nsPIDOMWindow* aWindow, - nsIPrincipal* aPrincipal) -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsRefPtr actor = - new PermissionRequestHelper(aWindow, aPrincipal); - return actor.forget().take(); -} - -bool -RecvPIndexedDBPermissionRequestConstructor( - PIndexedDBPermissionRequestParent* aActor) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aActor); - - auto* actor = static_cast(aActor); - - PermissionRequestBase::PermissionValue permission; - nsresult rv = actor->PromptIfNeeded(&permission); - if (NS_FAILED(rv)) { - return false; - } - - if (permission != PermissionRequestBase::kPermissionPrompt) { - unused << - PIndexedDBPermissionRequestParent::Send__delete__(actor, permission); - } - - return true; -} - -bool -DeallocPIndexedDBPermissionRequestParent( - PIndexedDBPermissionRequestParent* aActor) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aActor); - - nsRefPtr actor = - dont_AddRef(static_cast(aActor)); - return true; -} - -already_AddRefed -CreateQuotaClient() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsRefPtr client = new QuotaClient(); - return client.forget(); -} - -/******************************************************************************* - * Metadata classes - ******************************************************************************/ - -already_AddRefed -FullDatabaseMetadata::Duplicate() const -{ - AssertIsOnBackgroundThread(); - - class MOZ_STACK_CLASS IndexClosure MOZ_FINAL - { - FullObjectStoreMetadata& mNew; - - public: - explicit IndexClosure(FullObjectStoreMetadata& aNew) - : mNew(aNew) - { } - - static PLDHashOperator - Copy(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* closure = static_cast(aClosure); - - nsRefPtr newMetadata = new FullIndexMetadata(); - - newMetadata->mCommonMetadata = aValue->mCommonMetadata; - - if (NS_WARN_IF(!closure->mNew.mIndexes.Put(aKey, newMetadata, - fallible))) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; - } - }; - - class MOZ_STACK_CLASS ObjectStoreClosure MOZ_FINAL - { - FullDatabaseMetadata& mNew; - - public: - explicit ObjectStoreClosure(FullDatabaseMetadata& aNew) - : mNew(aNew) - { } - - static PLDHashOperator - Copy(const uint64_t& aKey, FullObjectStoreMetadata* aValue, void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* objClosure = static_cast(aClosure); - - nsRefPtr newMetadata = - new FullObjectStoreMetadata(); - - newMetadata->mCommonMetadata = aValue->mCommonMetadata; - newMetadata->mNextAutoIncrementId = aValue->mNextAutoIncrementId; - newMetadata->mComittedAutoIncrementId = aValue->mComittedAutoIncrementId; - - IndexClosure idxClosure(*newMetadata); - aValue->mIndexes.EnumerateRead(IndexClosure::Copy, &idxClosure); - - if (NS_WARN_IF(aValue->mIndexes.Count() != - newMetadata->mIndexes.Count())) { - return PL_DHASH_STOP; - } - - if (NS_WARN_IF(!objClosure->mNew.mObjectStores.Put(aKey, newMetadata, - fallible))) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; - } - }; - - // FullDatabaseMetadata contains two hash tables of pointers that we need to - // duplicate so we can't just use the copy constructor. - nsRefPtr newMetadata = - new FullDatabaseMetadata(mCommonMetadata); - - newMetadata->mDatabaseId = mDatabaseId; - newMetadata->mFilePath = mFilePath; - newMetadata->mNextObjectStoreId = mNextObjectStoreId; - newMetadata->mNextIndexId = mNextIndexId; - - ObjectStoreClosure closure(*newMetadata); - mObjectStores.EnumerateRead(ObjectStoreClosure::Copy, &closure); - - if (NS_WARN_IF(mObjectStores.Count() != - newMetadata->mObjectStores.Count())) { - return nullptr; - } - - return newMetadata.forget(); -} - -/******************************************************************************* - * Factory - ******************************************************************************/ - -uint64_t Factory::sFactoryInstanceCount = 0; - -Factory::Factory(const OptionalWindowId& aOptionalWindowId) - : mTransactionThreadPool(gCurrentTransactionThreadPool) - , mOptionalWindowId(aOptionalWindowId) - , mActorDestroyed(false) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransactionThreadPool); -} - -Factory::~Factory() -{ - MOZ_ASSERT(mActorDestroyed); - MOZ_ASSERT(!mTransactionThreadPool); -} - -// static -already_AddRefed -Factory::Create(const OptionalWindowId& aOptionalWindowId) -{ - AssertIsOnBackgroundThread(); - - nsRefPtr kungFuDeathGrip; - - // If this is the first instance then we need to do some initialization. - if (!sFactoryInstanceCount) { - MOZ_ASSERT(!gCurrentTransactionThreadPool); - - nsRefPtr threadPool = - TransactionThreadPool::Create(); - if (NS_WARN_IF(!threadPool)) { - return nullptr; - } - - gCurrentTransactionThreadPool = threadPool; - - // Hold this alive until the Factory constructor runs. - kungFuDeathGrip.swap(threadPool); - - MOZ_ASSERT(!gLiveDatabaseHashtable); - gLiveDatabaseHashtable = new DatabaseActorHashtable(); - - MOZ_ASSERT(!gStartTransactionRunnable); - gStartTransactionRunnable = new nsRunnable(); - -#ifdef DEBUG - if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { - NS_WARNING("PBackground thread debugging enabled, priority has been " - "modified!"); - nsCOMPtr thread = - do_QueryInterface(NS_GetCurrentThread()); - MOZ_ASSERT(thread); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->SetPriority(kDEBUGThreadPriority))); - } - - if (kDEBUGThreadSleepMS) { - NS_WARNING("PBackground thread debugging enabled, sleeping after every " - "event!"); - nsCOMPtr thread = - do_QueryInterface(NS_GetCurrentThread()); - MOZ_ASSERT(thread); - - gDEBUGThreadSlower = new DEBUGThreadSlower(); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->AddObserver(gDEBUGThreadSlower))); - } -#endif // DEBUG - } - - nsRefPtr actor = new Factory(aOptionalWindowId); - - sFactoryInstanceCount++; - - return actor.forget(); -} - -void -Factory::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; - - // Make sure this is released on this thread, but keep it alive past the call - // to TransactionThreadPool::ShutdownAsync() below. - nsRefPtr kungFuDeathGrip; - mTransactionThreadPool.swap(kungFuDeathGrip); - - // Clean up if there are no more instances. - if (!(--sFactoryInstanceCount)) { - if (gCurrentTransactionThreadPool) { - MOZ_ASSERT(gCurrentTransactionThreadPool == kungFuDeathGrip); - - gCurrentTransactionThreadPool->ShutdownAsync(); - - QuotaClient::GetInstance()->NoteDyingTransactionThreadPool(); - MOZ_ASSERT(!gCurrentTransactionThreadPool); - } - - MOZ_ASSERT(gStartTransactionRunnable); - gStartTransactionRunnable = nullptr; - - MOZ_ASSERT(gLiveDatabaseHashtable); - MOZ_ASSERT(!gLiveDatabaseHashtable->Count()); - gLiveDatabaseHashtable = nullptr; - -#ifdef DEBUG - if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { - nsCOMPtr thread = - do_QueryInterface(NS_GetCurrentThread()); - MOZ_ASSERT(thread); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL))); - } - - if (kDEBUGThreadSleepMS) { - MOZ_ASSERT(gDEBUGThreadSlower); - - nsCOMPtr thread = - do_QueryInterface(NS_GetCurrentThread()); - MOZ_ASSERT(thread); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->RemoveObserver(gDEBUGThreadSlower))); - - gDEBUGThreadSlower = nullptr; - } -#endif // DEBUG - } -} - -bool -Factory::RecvDeleteMe() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - return PBackgroundIDBFactoryParent::Send__delete__(this); -} - -PBackgroundIDBFactoryRequestParent* -Factory::AllocPBackgroundIDBFactoryRequestParent( - const FactoryRequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); - - const CommonFactoryRequestParams* commonParams; - - switch (aParams.type()) { - case FactoryRequestParams::TOpenDatabaseRequestParams: { - const OpenDatabaseRequestParams& params = - aParams.get_OpenDatabaseRequestParams(); - commonParams = ¶ms.commonParams(); - break; - } - - case FactoryRequestParams::TDeleteDatabaseRequestParams: { - const DeleteDatabaseRequestParams& params = - aParams.get_DeleteDatabaseRequestParams(); - commonParams = ¶ms.commonParams(); - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - MOZ_ASSERT(commonParams); - - const DatabaseMetadata& metadata = commonParams->metadata(); - if (NS_WARN_IF(metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT && - metadata.persistenceType() != PERSISTENCE_TYPE_TEMPORARY)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - const PrincipalInfo& principalInfo = commonParams->principalInfo(); - if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - nsRefPtr contentParent = - BackgroundParent::GetContentParent(Manager()); - - nsRefPtr actor; - if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) { - actor = new OpenDatabaseOp(this, - contentParent.forget(), - mOptionalWindowId, - *commonParams); - } else { - actor = new DeleteDatabaseOp(this, contentParent.forget(), *commonParams); - } - - // Transfer ownership to IPDL. - return actor.forget().take(); -} - -bool -Factory::RecvPBackgroundIDBFactoryRequestConstructor( - PBackgroundIDBFactoryRequestParent* aActor, - const FactoryRequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); - - auto* op = static_cast(aActor); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(op))); - return true; -} - -bool -Factory::DeallocPBackgroundIDBFactoryRequestParent( - PBackgroundIDBFactoryRequestParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - // Transfer ownership back from IPDL. - nsRefPtr op = dont_AddRef(static_cast(aActor)); - return true; -} - -PBackgroundIDBDatabaseParent* -Factory::AllocPBackgroundIDBDatabaseParent( - const DatabaseSpec& aSpec, - PBackgroundIDBFactoryRequestParent* aRequest) -{ - MOZ_CRASH("PBackgroundIDBDatabaseParent actors should be constructed " - "manually!"); -} - -bool -Factory::DeallocPBackgroundIDBDatabaseParent( - PBackgroundIDBDatabaseParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - nsRefPtr database = dont_AddRef(static_cast(aActor)); - return true; -} - -/******************************************************************************* - * Database - ******************************************************************************/ - -Database::Database(Factory* aFactory, - const PrincipalInfo& aPrincipalInfo, - const nsACString& aGroup, - const nsACString& aOrigin, - FullDatabaseMetadata* aMetadata, - FileManager* aFileManager, - already_AddRefed aOfflineStorage, - bool aChromeWriteAccessAllowed) - : mFactory(aFactory) - , mTransactionThreadPool(aFactory->GetTransactionThreadPool()) - , mMetadata(aMetadata) - , mFileManager(aFileManager) - , mOfflineStorage(Move(aOfflineStorage)) - , mPrincipalInfo(aPrincipalInfo) - , mGroup(aGroup) - , mOrigin(aOrigin) - , mId(aMetadata->mDatabaseId) - , mFilePath(aMetadata->mFilePath) - , mPersistenceType(aMetadata->mCommonMetadata.persistenceType()) - , mChromeWriteAccessAllowed(aChromeWriteAccessAllowed) - , mClosed(false) - , mInvalidated(false) - , mActorWasAlive(false) - , mActorDestroyed(false) - , mMetadataCleanedUp(false) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aFactory); - MOZ_ASSERT(mTransactionThreadPool); - MOZ_ASSERT(aMetadata); - MOZ_ASSERT(aFileManager); - MOZ_ASSERT_IF(aChromeWriteAccessAllowed, - aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo); - - mOfflineStorage->SetDatabase(this); -} - -void -Database::Invalidate() -{ - AssertIsOnBackgroundThread(); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static bool - InvalidateTransactions(nsTHashtable>& aTable) - { - AssertIsOnBackgroundThread(); - - const uint32_t count = aTable.Count(); - if (!count) { - return true; - } - - FallibleTArray> transactions; - if (NS_WARN_IF(!transactions.SetCapacity(count))) { - return false; - } - - aTable.EnumerateEntries(Collect, &transactions); - - if (NS_WARN_IF(transactions.Length() != count)) { - return false; - } - - if (count) { - IDB_REPORT_INTERNAL_ERR(); - - for (uint32_t index = 0; index < count; index++) { - nsRefPtr transaction = transactions[index].forget(); - MOZ_ASSERT(transaction); - - transaction->Invalidate(); - } - } - - return true; - } - - private: - static PLDHashOperator - Collect(nsPtrHashKey* aEntry, void* aUserData) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aUserData); - - auto* array = - static_cast>*>(aUserData); - - if (NS_WARN_IF(!array->AppendElement(aEntry->GetKey()))) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; - } - }; - - if (mInvalidated) { - return; - } - - mInvalidated = true; - - if (!mActorDestroyed) { - unused << SendInvalidate(); - } - - if (!Helper::InvalidateTransactions(mTransactions)) { - NS_WARNING("Failed to abort all transactions!"); - } - - MOZ_ALWAYS_TRUE(CloseInternal()); - - CleanupMetadata(); -} - -bool -Database::RegisterTransaction(TransactionBase* aTransaction) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(!mTransactions.GetEntry(aTransaction)); - MOZ_ASSERT(mOfflineStorage); - - if (NS_WARN_IF(!mTransactions.PutEntry(aTransaction, fallible))) { - return false; - } - - mOfflineStorage->NoteNewTransaction(); - return true; -} - -void -Database::UnregisterTransaction(TransactionBase* aTransaction) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(mTransactions.GetEntry(aTransaction)); - - mTransactions.RemoveEntry(aTransaction); - - if (mOfflineStorage) { - mOfflineStorage->NoteFinishedTransaction(); - - if (!mTransactions.Count() && IsClosed()) { - DatabaseOfflineStorage::UnregisterOnOwningThread( - mOfflineStorage.forget()); - CleanupMetadata(); - } - } -} - -void -Database::SetActorAlive() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorWasAlive); - MOZ_ASSERT(!mActorDestroyed); - - mActorWasAlive = true; - - // This reference will be absorbed by IPDL and released when the actor is - // destroyed. - AddRef(); -} - -bool -Database::CloseInternal() -{ - AssertIsOnBackgroundThread(); - - if (mClosed) { - if (NS_WARN_IF(!IsInvalidated())) { - // Kill misbehaving child for sending the close message twice. - return false; - } - - // Ignore harmless race when we just invalidated the database. - return true; - } - - mClosed = true; - - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info)); - - MOZ_ASSERT(info->mLiveDatabases.Contains(this)); - - if (info->mWaitingFactoryOp) { - info->mWaitingFactoryOp->NoteDatabaseClosed(this); - } - - if (mOfflineStorage) { - mOfflineStorage->CloseOnOwningThread(); - - if (!mTransactions.Count()) { - DatabaseOfflineStorage::UnregisterOnOwningThread( - mOfflineStorage.forget()); - CleanupMetadata(); - } - } - - return true; -} - -void -Database::CleanupMetadata() -{ - AssertIsOnBackgroundThread(); - - if (!mMetadataCleanedUp) { - mMetadataCleanedUp = true; - - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info)); - MOZ_ALWAYS_TRUE(info->mLiveDatabases.RemoveElement(this)); - - if (info->mLiveDatabases.IsEmpty()) { - MOZ_ASSERT(!info->mWaitingFactoryOp || - !info->mWaitingFactoryOp->HasBlockedDatabases()); - gLiveDatabaseHashtable->Remove(Id()); - } - } -} - -void -Database::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; - - // Make sure this is released on this thread. - mTransactionThreadPool = nullptr; - - if (!IsInvalidated()) { - Invalidate(); - } -} - -PBackgroundIDBDatabaseFileParent* -Database::AllocPBackgroundIDBDatabaseFileParent( - const BlobOrInputStream& aBlobOrInputStream) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aBlobOrInputStream.type() != BlobOrInputStream::T__None); - - nsRefPtr actor; - - switch (aBlobOrInputStream.type()) { - case BlobOrInputStream::TInputStreamParams: { - const InputStreamParams& inputStreamParams = - aBlobOrInputStream.get_InputStreamParams(); - MOZ_ASSERT(inputStreamParams.type() != InputStreamParams::T__None); - - nsRefPtr fileInfo = mFileManager->GetNewFileInfo(); - MOZ_ASSERT(fileInfo); - - actor = new DatabaseFile(inputStreamParams, fileInfo); - break; - } - - case BlobOrInputStream::TPBlobParent: { - auto* blobParent = - static_cast(aBlobOrInputStream.get_PBlobParent()); - if (NS_WARN_IF(!blobParent)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - nsRefPtr blobImpl = blobParent->GetBlobImpl(); - MOZ_ASSERT(blobImpl); - - nsRefPtr fileInfo = blobImpl->GetFileInfo(mFileManager); - MOZ_ASSERT(fileInfo); - - actor = new DatabaseFile(fileInfo); - break; - } - - case BlobOrInputStream::TPBlobChild: { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - MOZ_ASSERT(actor); - - return actor.forget().take(); -} - -bool -Database::DeallocPBackgroundIDBDatabaseFileParent( - PBackgroundIDBDatabaseFileParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - nsRefPtr actor = - dont_AddRef(static_cast(aActor)); - return true; -} - -PBackgroundIDBTransactionParent* -Database::AllocPBackgroundIDBTransactionParent( - const nsTArray& aObjectStoreNames, - const Mode& aMode) -{ - AssertIsOnBackgroundThread(); - - class MOZ_STACK_CLASS Closure MOZ_FINAL - { - const nsString& mName; - FallibleTArray>& mObjectStores; - - public: - Closure(const nsString& aName, - FallibleTArray>& aObjectStores) - : mName(aName) - , mObjectStores(aObjectStores) - { } - - static PLDHashOperator - Find(const uint64_t& aKey, - FullObjectStoreMetadata* aValue, - void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* closure = static_cast(aClosure); - - if (closure->mName == aValue->mCommonMetadata.name() && - !aValue->mDeleted) { - MOZ_ALWAYS_TRUE(closure->mObjectStores.AppendElement(aValue)); - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; - } - }; - - // Once a database is closed it must not try to open new transactions. - if (NS_WARN_IF(mClosed)) { - if (!mInvalidated) { - ASSERT_UNLESS_FUZZING(); - } - return nullptr; - } - - if (NS_WARN_IF(aObjectStoreNames.IsEmpty())) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY && - aMode != IDBTransaction::READ_WRITE)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - // If this is a readwrite transaction to a chrome database make sure the child - // has write access. - if (NS_WARN_IF(aMode == IDBTransaction::READ_WRITE && - mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo && - !mChromeWriteAccessAllowed)) { - return nullptr; - } - - const ObjectStoreTable& objectStores = mMetadata->mObjectStores; - const uint32_t nameCount = aObjectStoreNames.Length(); - - if (NS_WARN_IF(nameCount > objectStores.Count())) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - FallibleTArray> fallibleObjectStores; - if (NS_WARN_IF(!fallibleObjectStores.SetCapacity(nameCount))) { - return nullptr; - } - - for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) { - const nsString& name = aObjectStoreNames[nameIndex]; - const uint32_t oldLength = fallibleObjectStores.Length(); - - Closure closure(name, fallibleObjectStores); - objectStores.EnumerateRead(Closure::Find, &closure); - - if (NS_WARN_IF((oldLength + 1) != fallibleObjectStores.Length())) { - return nullptr; - } - } - - nsTArray> infallibleObjectStores; - infallibleObjectStores.SwapElements(fallibleObjectStores); - - nsRefPtr transaction = - new NormalTransaction(this, infallibleObjectStores, aMode); - - MOZ_ASSERT(infallibleObjectStores.IsEmpty()); - - return transaction.forget().take(); -} - -bool -Database::RecvPBackgroundIDBTransactionConstructor( - PBackgroundIDBTransactionParent* aActor, - const nsTArray& aObjectStoreNames, - const Mode& aMode) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(!aObjectStoreNames.IsEmpty()); - MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY || - aMode == IDBTransaction::READ_WRITE); - MOZ_ASSERT(!mClosed); - - if (IsInvalidated()) { - // This is an expected race. We don't want the child to die here, just don't - // actually do any work. - return true; - } - - auto* transaction = static_cast(aActor); - - TransactionThreadPool* threadPool = GetTransactionThreadPool(); - MOZ_ASSERT(threadPool); - - // Add a placeholder for this transaction immediately. - threadPool->Dispatch(transaction->TransactionId(), - mMetadata->mDatabaseId, - aObjectStoreNames, - aMode, - gStartTransactionRunnable, - /* aFinish */ false, - /* aFinishCallback */ nullptr); - - transaction->SetActive(); - - if (NS_WARN_IF(!RegisterTransaction(transaction))) { - IDB_REPORT_INTERNAL_ERR(); - transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false); - return true; - } - - return true; -} - -bool -Database::DeallocPBackgroundIDBTransactionParent( - PBackgroundIDBTransactionParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - nsRefPtr transaction = - dont_AddRef(static_cast(aActor)); - return true; -} - -PBackgroundIDBVersionChangeTransactionParent* -Database::AllocPBackgroundIDBVersionChangeTransactionParent( - const uint64_t& aCurrentVersion, - const uint64_t& aRequestedVersion, - const int64_t& aNextObjectStoreId, - const int64_t& aNextIndexId) -{ - MOZ_CRASH("PBackgroundIDBVersionChangeTransactionParent actors should be " - "constructed manually!"); -} - -bool -Database::DeallocPBackgroundIDBVersionChangeTransactionParent( - PBackgroundIDBVersionChangeTransactionParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - nsRefPtr transaction = - dont_AddRef(static_cast(aActor)); - return true; -} - -bool -Database::RecvDeleteMe() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - return PBackgroundIDBDatabaseParent::Send__delete__(this); -} - -bool -Database::RecvBlocked() -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(mClosed)) { - return false; - } - - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info)); - - MOZ_ASSERT(info->mLiveDatabases.Contains(this)); - MOZ_ASSERT(info->mWaitingFactoryOp); - - info->mWaitingFactoryOp->NoteDatabaseBlocked(this); - - return true; -} - -bool -Database::RecvClose() -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(!CloseInternal())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - return true; -} - -/******************************************************************************* - * DatabaseFile - ******************************************************************************/ - -already_AddRefed -DatabaseFile::GetInputStream() const -{ - if (mInputStreamParams.type() == InputStreamParams::T__None) { - return nullptr; - } - - nsTArray fileDescriptors; - nsCOMPtr inputStream = - DeserializeInputStream(mInputStreamParams, fileDescriptors); - - if (NS_WARN_IF(!inputStream)) { - return nullptr; - } - - MOZ_ASSERT(fileDescriptors.IsEmpty()); - - return inputStream.forget(); -} - -void -DatabaseFile::ClearInputStreamParams() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mInputStreamParams.type() != InputStreamParams::T__None); - - mInputStreamParams = InputStreamParams(); -} - -void -DatabaseFile::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - - mFileInfo = nullptr; - mInputStreamParams = InputStreamParams(); -} - -/******************************************************************************* - * TransactionBase - ******************************************************************************/ - -TransactionBase::TransactionBase(Database* aDatabase, - Mode aMode) - : mDatabase(aDatabase) - , mTransactionThreadPool(aDatabase->GetTransactionThreadPool()) - , mTransactionId(mTransactionThreadPool->NextTransactionId()) - , mDatabaseId(aDatabase->Id()) - , mActiveRequestCount(0) - , mInvalidatedOnAnyThread(false) - , mMode(aMode) - , mHasBeenActive(false) - , mActorDestroyed(false) - , mInvalidated(false) - , mResultCode(NS_OK) - , mCommitOrAbortReceived(false) - , mCommittedOrAborted(false) - , mForceAborted(false) - , mTransactionThread(nullptr) - , mSavepointCount(0) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aDatabase); - MOZ_ASSERT(mTransactionThreadPool); -} - -TransactionBase::~TransactionBase() -{ - MOZ_ASSERT(!mSavepointCount); - MOZ_ASSERT(!mActiveRequestCount); - MOZ_ASSERT(mActorDestroyed); - MOZ_ASSERT_IF(mHasBeenActive, mCommittedOrAborted); -} - -nsresult -TransactionBase::EnsureConnection() -{ -#ifdef DEBUG - MOZ_ASSERT(!IsOnBackgroundThread()); - if (!mTransactionThread) { - mTransactionThread = PR_GetCurrentThread(); - } -#endif - - AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "TransactionBase::EnsureConnection", - js::ProfileEntry::Category::STORAGE); - - if (!mConnection) { - nsCOMPtr connection; - nsresult rv = - GetDatabaseConnection(mDatabase->FilePath(), mDatabase->Type(), - mDatabase->Group(), mDatabase->Origin(), - getter_AddRefs(connection)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsRefPtr function; - nsCString beginTransaction; - - if (mMode == IDBTransaction::READ_ONLY) { - beginTransaction.AssignLiteral("BEGIN TRANSACTION;"); - } else { - function = new UpdateRefcountFunction(mDatabase->GetFileManager()); - - rv = connection->CreateFunction(NS_LITERAL_CSTRING("update_refcount"), 2, - function); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;"); - } - - nsCOMPtr stmt; - rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - function.swap(mUpdateFileRefcountFunction); - connection.swap(mConnection); - } - - return NS_OK; -} - -void -TransactionBase::Abort(nsresult aResultCode, bool aForce) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(NS_FAILED(aResultCode)); - - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = aResultCode; - } - - if (aForce) { - mForceAborted = true; - } - - MaybeCommitOrAbort(); -} - -bool -TransactionBase::RecvCommit() -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - mCommitOrAbortReceived = true; - - MaybeCommitOrAbort(); - return true; -} - -bool -TransactionBase::RecvAbort(nsresult aResultCode) -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(NS_SUCCEEDED(aResultCode))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode) != - NS_ERROR_MODULE_DOM_INDEXEDDB)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - mCommitOrAbortReceived = true; - - Abort(aResultCode, /* aForce */ false); - return true; -} - -void -TransactionBase::CommitOrAbort() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mCommittedOrAborted); - - mCommittedOrAborted = true; - - if (!mHasBeenActive) { - return; - } - - nsRefPtr commitOp = - new CommitOp(this, ClampResultCode(mResultCode)); - - TransactionThreadPool* threadPool = GetTransactionThreadPool(); - MOZ_ASSERT(threadPool); - - threadPool->Dispatch(TransactionId(), - DatabaseId(), - commitOp, - /* aFinish */ true, - /* aFinishCallback */ commitOp); -} - -already_AddRefed -TransactionBase::GetMetadataForObjectStoreId(int64_t aObjectStoreId) const -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aObjectStoreId); - - if (!aObjectStoreId) { - return nullptr; - } - - nsRefPtr metadata; - if (!mDatabase->Metadata()->mObjectStores.Get(aObjectStoreId, - getter_AddRefs(metadata))) { - return nullptr; - } - - MOZ_ASSERT(metadata->mCommonMetadata.id() == aObjectStoreId); - MOZ_ASSERT(!metadata->mDeleted); - - return metadata.forget(); -} - -already_AddRefed -TransactionBase::GetMetadataForIndexId( - FullObjectStoreMetadata* const aObjectStoreMetadata, - int64_t aIndexId) const -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aIndexId); - - if (!aIndexId) { - return nullptr; - } - - nsRefPtr metadata; - if (!aObjectStoreMetadata->mIndexes.Get(aIndexId, getter_AddRefs(metadata))) { - return nullptr; - } - - MOZ_ASSERT(metadata->mCommonMetadata.id() == aIndexId); - MOZ_ASSERT(!metadata->mDeleted); - - return metadata.forget(); -} - -void -TransactionBase::NoteModifiedAutoIncrementObjectStore( - FullObjectStoreMetadata* aMetadata) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aMetadata); - - if (!mModifiedAutoIncrementObjectStoreMetadataArray.Contains(aMetadata)) { - mModifiedAutoIncrementObjectStoreMetadataArray.AppendElement(aMetadata); - } -} - -void -TransactionBase::ForgetModifiedAutoIncrementObjectStore( - FullObjectStoreMetadata* aMetadata) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aMetadata); - - mModifiedAutoIncrementObjectStoreMetadataArray.RemoveElement(aMetadata); -} - -bool -TransactionBase::VerifyRequestParams(const RequestParams& aParams) const -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - - switch (aParams.type()) { - case RequestParams::TObjectStoreAddParams: { - const ObjectStoreAddPutParams& params = - aParams.get_ObjectStoreAddParams().commonParams(); - if (NS_WARN_IF(!VerifyRequestParams(params))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStorePutParams: { - const ObjectStoreAddPutParams& params = - aParams.get_ObjectStorePutParams().commonParams(); - if (NS_WARN_IF(!VerifyRequestParams(params))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStoreGetParams: { - const ObjectStoreGetParams& params = aParams.get_ObjectStoreGetParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStoreGetAllParams: { - const ObjectStoreGetAllParams& params = - aParams.get_ObjectStoreGetAllParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStoreGetAllKeysParams: { - const ObjectStoreGetAllKeysParams& params = - aParams.get_ObjectStoreGetAllKeysParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStoreDeleteParams: { - const ObjectStoreDeleteParams& params = - aParams.get_ObjectStoreDeleteParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStoreClearParams: { - const ObjectStoreClearParams& params = - aParams.get_ObjectStoreClearParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TObjectStoreCountParams: { - const ObjectStoreCountParams& params = - aParams.get_ObjectStoreCountParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - - case RequestParams::TIndexGetParams: { - const IndexGetParams& params = aParams.get_IndexGetParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - const nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TIndexGetKeyParams: { - const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - const nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TIndexGetAllParams: { - const IndexGetAllParams& params = aParams.get_IndexGetAllParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - const nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TIndexGetAllKeysParams: { - const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - const nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case RequestParams::TIndexCountParams: { - const IndexCountParams& params = aParams.get_IndexCountParams(); - const nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - const nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - return true; -} - -bool -TransactionBase::VerifyRequestParams(const OpenCursorParams& aParams) const -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); - - switch (aParams.type()) { - case OpenCursorParams::TObjectStoreOpenCursorParams: { - const ObjectStoreOpenCursorParams& params = - aParams.get_ObjectStoreOpenCursorParams(); - nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case OpenCursorParams::TObjectStoreOpenKeyCursorParams: { - const ObjectStoreOpenKeyCursorParams& params = - aParams.get_ObjectStoreOpenKeyCursorParams(); - nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case OpenCursorParams::TIndexOpenCursorParams: { - const IndexOpenCursorParams& params = aParams.get_IndexOpenCursorParams(); - nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - case OpenCursorParams::TIndexOpenKeyCursorParams: { - const IndexOpenKeyCursorParams& params = - aParams.get_IndexOpenKeyCursorParams(); - nsRefPtr objectStoreMetadata = - GetMetadataForObjectStoreId(params.objectStoreId()); - if (NS_WARN_IF(!objectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - nsRefPtr indexMetadata = - GetMetadataForIndexId(objectStoreMetadata, params.indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - return true; -} - -bool -TransactionBase::VerifyRequestParams(const CursorRequestParams& aParams) const -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None); - - switch (aParams.type()) { - case CursorRequestParams::TContinueParams: - break; - - case CursorRequestParams::TAdvanceParams: - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - return true; -} - -bool -TransactionBase::VerifyRequestParams(const SerializedKeyRange& aParams) const -{ - AssertIsOnBackgroundThread(); - - // XXX Check more here? - - if (aParams.isOnly()) { - if (NS_WARN_IF(aParams.lower().IsUnset())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(!aParams.upper().IsUnset())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(aParams.lowerOpen())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - if (NS_WARN_IF(aParams.upperOpen())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - } else if (NS_WARN_IF(aParams.lower().IsUnset() && - aParams.upper().IsUnset())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - return true; -} - -bool -TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams) - const -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE && - mMode != IDBTransaction::VERSION_CHANGE)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr objMetadata = - GetMetadataForObjectStoreId(aParams.objectStoreId()); - if (NS_WARN_IF(!objMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(aParams.cloneInfo().data().IsEmpty())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (objMetadata->mCommonMetadata.autoIncrement() && - objMetadata->mCommonMetadata.keyPath().IsValid() && - aParams.key().IsUnset()) { - const SerializedStructuredCloneWriteInfo cloneInfo = aParams.cloneInfo(); - - if (NS_WARN_IF(!cloneInfo.offsetToKeyProp())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(cloneInfo.data().Length() < sizeof(uint64_t))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(cloneInfo.offsetToKeyProp() > - (cloneInfo.data().Length() - sizeof(uint64_t)))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - } else if (NS_WARN_IF(aParams.cloneInfo().offsetToKeyProp())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - const nsTArray& updates = aParams.indexUpdateInfos(); - - for (uint32_t index = 0; index < updates.Length(); index++) { - nsRefPtr indexMetadata = - GetMetadataForIndexId(objMetadata, updates[index].indexId()); - if (NS_WARN_IF(!indexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(updates[index].value().IsUnset())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - } - - const nsTArray& files = aParams.files(); - - for (uint32_t index = 0; index < files.Length(); index++) { - const DatabaseFileOrMutableFileId& fileOrFileId = files[index]; - - MOZ_ASSERT(fileOrFileId.type() != DatabaseFileOrMutableFileId::T__None); - - switch (fileOrFileId.type()) { - case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileChild: - ASSERT_UNLESS_FUZZING(); - return false; - - case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileParent: - if (NS_WARN_IF(!fileOrFileId.get_PBackgroundIDBDatabaseFileParent())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - - case DatabaseFileOrMutableFileId::Tint64_t: - if (NS_WARN_IF(fileOrFileId.get_int64_t() <= 0)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - - default: - MOZ_CRASH("Should never get here!"); - } - } - - return true; -} - -bool -TransactionBase::VerifyRequestParams(const OptionalKeyRange& aParams) const -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != OptionalKeyRange::T__None); - - switch (aParams.type()) { - case OptionalKeyRange::TSerializedKeyRange: - if (NS_WARN_IF(!VerifyRequestParams(aParams.get_SerializedKeyRange()))) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - - case OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - return true; -} - -nsresult -TransactionBase::StartSavepoint() -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(mConnection); - MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode || - IDBTransaction::VERSION_CHANGE == mMode); - - CachedStatement stmt; - nsresult rv = GetCachedStatement(kSavepointClause, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mUpdateFileRefcountFunction->StartSavepoint(); - - mSavepointCount++; - - return NS_OK; -} - -nsresult -TransactionBase::ReleaseSavepoint() -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(mConnection); - MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode || - IDBTransaction::VERSION_CHANGE == mMode); - MOZ_ASSERT(mSavepointCount); - - mSavepointCount--; - - CachedStatement stmt; - nsresult rv = GetCachedStatement( - NS_LITERAL_CSTRING("RELEASE ") + NS_LITERAL_CSTRING(kSavepointClause), - &stmt); - if (NS_SUCCEEDED(rv)) { - rv = stmt->Execute(); - if (NS_SUCCEEDED(rv)) { - mUpdateFileRefcountFunction->ReleaseSavepoint(); - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - mUpdateFileRefcountFunction->RollbackSavepoint(); - } - - return rv; -} - -nsresult -TransactionBase::RollbackSavepoint() -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(mConnection); - MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode || - IDBTransaction::VERSION_CHANGE == mMode); - MOZ_ASSERT(mSavepointCount); - - mSavepointCount--; - - mUpdateFileRefcountFunction->RollbackSavepoint(); - - CachedStatement stmt; - nsresult rv = GetCachedStatement( - NS_LITERAL_CSTRING("ROLLBACK TO ") + NS_LITERAL_CSTRING(kSavepointClause), - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // This may fail if SQLite already rolled back the savepoint so ignore any - // errors. - unused << stmt->Execute(); - - return NS_OK; -} - -void -TransactionBase::NoteActiveRequest() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mActiveRequestCount < UINT64_MAX); - - mActiveRequestCount++; -} - -void -TransactionBase::NoteFinishedRequest() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mActiveRequestCount); - - mActiveRequestCount--; - - MaybeCommitOrAbort(); -} - -void -TransactionBase::Invalidate() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread); - - if (!mInvalidated) { - mInvalidated = true; - mInvalidatedOnAnyThread = true; - - Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ true); - } -} - -PBackgroundIDBRequestParent* -TransactionBase::AllocRequest(const RequestParams& aParams, bool aTrustParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - -#ifdef DEBUG - // Always verify parameters in DEBUG builds! - aTrustParams = false; -#endif - - if (!aTrustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - nsRefPtr actor; - - switch (aParams.type()) { - case RequestParams::TObjectStoreAddParams: - case RequestParams::TObjectStorePutParams: - actor = new ObjectStoreAddOrPutRequestOp(this, aParams); - break; - - case RequestParams::TObjectStoreGetParams: - actor = - new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ false); - break; - - case RequestParams::TObjectStoreGetAllParams: - actor = - new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ true); - break; - - case RequestParams::TObjectStoreGetAllKeysParams: - actor = - new ObjectStoreGetAllKeysRequestOp(this, - aParams.get_ObjectStoreGetAllKeysParams()); - break; - - case RequestParams::TObjectStoreDeleteParams: - actor = - new ObjectStoreDeleteRequestOp(this, - aParams.get_ObjectStoreDeleteParams()); - break; - - case RequestParams::TObjectStoreClearParams: - actor = - new ObjectStoreClearRequestOp(this, - aParams.get_ObjectStoreClearParams()); - break; - - case RequestParams::TObjectStoreCountParams: - actor = - new ObjectStoreCountRequestOp(this, - aParams.get_ObjectStoreCountParams()); - break; - - case RequestParams::TIndexGetParams: - actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ false); - break; - - case RequestParams::TIndexGetKeyParams: - actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ false); - break; - - case RequestParams::TIndexGetAllParams: - actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ true); - break; - - case RequestParams::TIndexGetAllKeysParams: - actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ true); - break; - - case RequestParams::TIndexCountParams: - actor = new IndexCountRequestOp(this, aParams); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - MOZ_ASSERT(actor); - - // Transfer ownership to IPDL. - return actor.forget().take(); -} - -bool -TransactionBase::StartRequest(PBackgroundIDBRequestParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - auto* op = static_cast(aActor); - - if (NS_WARN_IF(!op->Init(this))) { - op->Cleanup(); - return false; - } - - op->DispatchToTransactionThreadPool(); - return true; -} - -bool -TransactionBase::DeallocRequest(PBackgroundIDBRequestParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - // Transfer ownership back from IPDL. - nsRefPtr actor = - dont_AddRef(static_cast(aActor)); - return true; -} - -PBackgroundIDBCursorParent* -TransactionBase::AllocCursor(const OpenCursorParams& aParams, bool aTrustParams) -{ - AssertIsOnBackgroundThread(); - -#ifdef DEBUG - // Always verify parameters in DEBUG builds! - aTrustParams = false; -#endif - - if (!aTrustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - OpenCursorParams::Type type = aParams.type(); - MOZ_ASSERT(type != OpenCursorParams::T__None); - - int64_t objectStoreId; - int64_t indexId; - Cursor::Direction direction; - - switch(type) { - case OpenCursorParams::TObjectStoreOpenCursorParams: { - const auto& params = aParams.get_ObjectStoreOpenCursorParams(); - objectStoreId = params.objectStoreId(); - indexId = 0; - direction = params.direction(); - break; - } - - case OpenCursorParams::TObjectStoreOpenKeyCursorParams: { - const auto& params = aParams.get_ObjectStoreOpenKeyCursorParams(); - objectStoreId = params.objectStoreId(); - indexId = 0; - direction = params.direction(); - break; - } - - case OpenCursorParams::TIndexOpenCursorParams: { - const auto& params = aParams.get_IndexOpenCursorParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - direction = params.direction(); - break; - } - - case OpenCursorParams::TIndexOpenKeyCursorParams: { - const auto& params = aParams.get_IndexOpenKeyCursorParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - direction = params.direction(); - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - nsRefPtr actor = - new Cursor(this, type, objectStoreId, indexId, direction); - - // Transfer ownership to IPDL. - return actor.forget().take(); -} - -bool -TransactionBase::StartCursor(PBackgroundIDBCursorParent* aActor, - const OpenCursorParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); - - auto* op = static_cast(aActor); - - if (NS_WARN_IF(!op->Start(aParams))) { - return false; - } - - return true; -} - -bool -TransactionBase::DeallocCursor(PBackgroundIDBCursorParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - // Transfer ownership back from IPDL. - nsRefPtr actor = dont_AddRef(static_cast(aActor)); - return true; -} - -nsresult -TransactionBase::GetCachedStatement(const nsACString& aQuery, - CachedStatement* aCachedStatement) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(!aQuery.IsEmpty()); - MOZ_ASSERT(aCachedStatement); - MOZ_ASSERT(mConnection); - - nsCOMPtr stmt; - - if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) { - nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt)); - if (NS_FAILED(rv)) { -#ifdef DEBUG - nsCString msg; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->GetLastErrorString(msg))); - - nsAutoCString error = - NS_LITERAL_CSTRING("The statement '") + aQuery + - NS_LITERAL_CSTRING("' failed to compile with the error message '") + - msg + NS_LITERAL_CSTRING("'."); - - NS_WARNING(error.get()); -#endif - return rv; - } - - mCachedStatements.Put(aQuery, stmt); - } - - aCachedStatement->Assign(stmt.forget()); - return NS_OK; -} - -void -TransactionBase::ReleaseTransactionThreadObjects() -{ - AssertIsOnTransactionThread(); - - mCachedStatements.Clear(); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->Close())); - mConnection = nullptr; -} - -void -TransactionBase::ReleaseBackgroundThreadObjects() -{ - AssertIsOnBackgroundThread(); - - if (mUpdateFileRefcountFunction) { - mUpdateFileRefcountFunction->ClearFileInfoEntries(); - mUpdateFileRefcountFunction = nullptr; - } - - mTransactionThreadPool = nullptr; -} - -/******************************************************************************* - * NormalTransaction - ******************************************************************************/ - -NormalTransaction::NormalTransaction( - Database* aDatabase, - nsTArray>& aObjectStores, - TransactionBase::Mode aMode) - : TransactionBase(aDatabase, aMode) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!aObjectStores.IsEmpty()); - - mObjectStores.SwapElements(aObjectStores); -} - -bool -NormalTransaction::IsSameProcessActor() -{ - AssertIsOnBackgroundThread(); - - PBackgroundParent* actor = Manager()->Manager()->Manager(); - MOZ_ASSERT(actor); - - return !BackgroundParent::IsOtherProcessActor(actor); -} - -bool -NormalTransaction::SendCompleteNotification(nsresult aResult) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!IsActorDestroyed()); - - if (NS_WARN_IF(!SendComplete(aResult))) { - return false; - } - - return true; -} - -void -NormalTransaction::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - - if (!mCommittedOrAborted) { - if (NS_SUCCEEDED(mResultCode)) { - IDB_REPORT_INTERNAL_ERR(); - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mForceAborted = true; - - MaybeCommitOrAbort(); - } - - NoteActorDestroyed(); -} - -bool -NormalTransaction::RecvDeleteMe() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!IsActorDestroyed()); - - return PBackgroundIDBTransactionParent::Send__delete__(this); -} - -bool -NormalTransaction::RecvCommit() -{ - AssertIsOnBackgroundThread(); - - return TransactionBase::RecvCommit(); -} - -bool -NormalTransaction::RecvAbort(const nsresult& aResultCode) -{ - AssertIsOnBackgroundThread(); - - return TransactionBase::RecvAbort(aResultCode); -} - -PBackgroundIDBRequestParent* -NormalTransaction::AllocPBackgroundIDBRequestParent( - const RequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - - return AllocRequest(aParams, IsSameProcessActor()); -} - -bool -NormalTransaction::RecvPBackgroundIDBRequestConstructor( - PBackgroundIDBRequestParent* aActor, - const RequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - - return StartRequest(aActor); -} - -bool -NormalTransaction::DeallocPBackgroundIDBRequestParent( - PBackgroundIDBRequestParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - return DeallocRequest(aActor); -} - -PBackgroundIDBCursorParent* -NormalTransaction::AllocPBackgroundIDBCursorParent( - const OpenCursorParams& aParams) -{ - AssertIsOnBackgroundThread(); - - return AllocCursor(aParams, IsSameProcessActor()); -} - -bool -NormalTransaction::RecvPBackgroundIDBCursorConstructor( - PBackgroundIDBCursorParent* aActor, - const OpenCursorParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); - - return StartCursor(aActor, aParams); -} - -bool -NormalTransaction::DeallocPBackgroundIDBCursorParent( - PBackgroundIDBCursorParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - return DeallocCursor(aActor); -} - -/******************************************************************************* - * VersionChangeTransaction - ******************************************************************************/ - -VersionChangeTransaction::VersionChangeTransaction( - OpenDatabaseOp* aOpenDatabaseOp) - : TransactionBase(aOpenDatabaseOp->mDatabase, IDBTransaction::VERSION_CHANGE) - , mOpenDatabaseOp(aOpenDatabaseOp) - , mActorWasAlive(false) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aOpenDatabaseOp); -} - -VersionChangeTransaction::~VersionChangeTransaction() -{ -#ifdef DEBUG - // Silence the base class' destructor assertion if we never made this actor - // live. - FakeActorDestroyed(); -#endif -} - -bool -VersionChangeTransaction::IsSameProcessActor() -{ - AssertIsOnBackgroundThread(); - - PBackgroundParent* actor = Manager()->Manager()->Manager(); - MOZ_ASSERT(actor); - - return !BackgroundParent::IsOtherProcessActor(actor); -} - -void -VersionChangeTransaction::SetActorAlive() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorWasAlive); - MOZ_ASSERT(!IsActorDestroyed()); - - mActorWasAlive = true; - - // This reference will be absorbed by IPDL and released when the actor is - // destroyed. - AddRef(); -} - -bool -VersionChangeTransaction::CopyDatabaseMetadata() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mOldMetadata); - - const nsRefPtr origMetadata = - GetDatabase()->Metadata(); - MOZ_ASSERT(origMetadata); - - nsRefPtr newMetadata = origMetadata->Duplicate(); - if (NS_WARN_IF(!newMetadata)) { - return false; - } - - // Replace the live metadata with the new mutable copy. - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(origMetadata->mDatabaseId, - &info)); - MOZ_ASSERT(!info->mLiveDatabases.IsEmpty()); - MOZ_ASSERT(info->mMetadata == origMetadata); - - mOldMetadata = info->mMetadata.forget(); - info->mMetadata.swap(newMetadata); - - // Replace metadata pointers for all live databases. - for (uint32_t count = info->mLiveDatabases.Length(), index = 0; - index < count; - index++) { - info->mLiveDatabases[index]->mMetadata = info->mMetadata; - } - - return true; -} - -void -VersionChangeTransaction::UpdateMetadata(nsresult aResult) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(GetDatabase()); - MOZ_ASSERT(mOpenDatabaseOp); - MOZ_ASSERT(mOpenDatabaseOp->mDatabase); - MOZ_ASSERT(!mOpenDatabaseOp->mDatabaseId.IsEmpty()); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static PLDHashOperator - Enumerate(const uint64_t& aKey, - nsRefPtr& aValue, - void* /* aClosure */) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - - if (aValue->mDeleted) { - return PL_DHASH_REMOVE; - } - - aValue->mIndexes.Enumerate(Enumerate, nullptr); -#ifdef DEBUG - aValue->mIndexes.MarkImmutable(); -#endif - - return PL_DHASH_NEXT; - } - - private: - static PLDHashOperator - Enumerate(const uint64_t& aKey, - nsRefPtr& aValue, - void* /* aClosure */) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - - if (aValue->mDeleted) { - return PL_DHASH_REMOVE; - } - - return PL_DHASH_NEXT; - } - }; - - if (IsActorDestroyed()) { - return; - } - - nsRefPtr oldMetadata; - mOldMetadata.swap(oldMetadata); - - DatabaseActorInfo* info; - if (!gLiveDatabaseHashtable->Get(oldMetadata->mDatabaseId, &info)) { - return; - } - - MOZ_ASSERT(!info->mLiveDatabases.IsEmpty()); - - if (NS_SUCCEEDED(aResult)) { - // Remove all deleted objectStores and indexes, then mark immutable. - info->mMetadata->mObjectStores.Enumerate(Helper::Enumerate, nullptr); -#ifdef DEBUG - info->mMetadata->mObjectStores.MarkImmutable(); -#endif - } else { - // Replace metadata pointers for all live databases. - info->mMetadata = oldMetadata.forget(); - - for (uint32_t count = info->mLiveDatabases.Length(), index = 0; - index < count; - index++) { - info->mLiveDatabases[index]->mMetadata = info->mMetadata; - } - } -} - -bool -VersionChangeTransaction::SendCompleteNotification(nsresult aResult) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mOpenDatabaseOp); - MOZ_ASSERT(!IsActorDestroyed()); - - nsRefPtr openDatabaseOp; - mOpenDatabaseOp.swap(openDatabaseOp); - - if (NS_FAILED(aResult) && NS_SUCCEEDED(openDatabaseOp->mResultCode)) { - openDatabaseOp->mResultCode = aResult; - } - - openDatabaseOp->mState = OpenDatabaseOp::State_SendingResults; - - bool result = SendComplete(aResult); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(openDatabaseOp->Run())); - - return result; -} - -void -VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - - if (!mCommittedOrAborted) { - if (NS_SUCCEEDED(mResultCode)) { - IDB_REPORT_INTERNAL_ERR(); - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mForceAborted = true; - - MaybeCommitOrAbort(); - } - - NoteActorDestroyed(); -} - -bool -VersionChangeTransaction::RecvDeleteMe() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!IsActorDestroyed()); - - return PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this); -} - -bool -VersionChangeTransaction::RecvCommit() -{ - AssertIsOnBackgroundThread(); - - return TransactionBase::RecvCommit(); -} - -bool -VersionChangeTransaction::RecvAbort(const nsresult& aResultCode) -{ - AssertIsOnBackgroundThread(); - - return TransactionBase::RecvAbort(aResultCode); -} - -bool -VersionChangeTransaction::RecvCreateObjectStore( - const ObjectStoreMetadata& aMetadata) -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(!aMetadata.id())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - const nsRefPtr dbMetadata = GetDatabase()->Metadata(); - MOZ_ASSERT(dbMetadata); - - if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextObjectStoreId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - auto* foundMetadata = - MetadataNameOrIdMatcher::Match( - dbMetadata->mObjectStores, aMetadata.id(), aMetadata.name()); - - if (NS_WARN_IF(foundMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr newMetadata = new FullObjectStoreMetadata(); - newMetadata->mCommonMetadata = aMetadata; - newMetadata->mNextAutoIncrementId = aMetadata.autoIncrement() ? 1 : 0; - newMetadata->mComittedAutoIncrementId = newMetadata->mNextAutoIncrementId; - - if (NS_WARN_IF(!dbMetadata->mObjectStores.Put(aMetadata.id(), newMetadata, - fallible))) { - return false; - } - - dbMetadata->mNextObjectStoreId++; - - nsRefPtr op = new CreateObjectStoreOp(this, aMetadata); - - if (NS_WARN_IF(!op->Init(this))) { - op->Cleanup(); - return false; - } - - op->DispatchToTransactionThreadPool(); - - return true; -} - -bool -VersionChangeTransaction::RecvDeleteObjectStore(const int64_t& aObjectStoreId) -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(!aObjectStoreId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - const nsRefPtr dbMetadata = GetDatabase()->Metadata(); - MOZ_ASSERT(dbMetadata); - MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0); - - if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr foundMetadata = - GetMetadataForObjectStoreId(aObjectStoreId); - - if (NS_WARN_IF(!foundMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - foundMetadata->mDeleted = true; - - nsRefPtr op = - new DeleteObjectStoreOp(this, foundMetadata); - - if (NS_WARN_IF(!op->Init(this))) { - op->Cleanup(); - return false; - } - - op->DispatchToTransactionThreadPool(); - - return true; -} - -bool -VersionChangeTransaction::RecvCreateIndex(const int64_t& aObjectStoreId, - const IndexMetadata& aMetadata) -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(!aObjectStoreId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(!aMetadata.id())) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - const nsRefPtr dbMetadata = GetDatabase()->Metadata(); - MOZ_ASSERT(dbMetadata); - - if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextIndexId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr foundObjectStoreMetadata = - GetMetadataForObjectStoreId(aObjectStoreId); - - if (NS_WARN_IF(!foundObjectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr foundIndexMetadata = - MetadataNameOrIdMatcher::Match( - foundObjectStoreMetadata->mIndexes, aMetadata.id(), aMetadata.name()); - - if (NS_WARN_IF(foundIndexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr newMetadata = new FullIndexMetadata(); - newMetadata->mCommonMetadata = aMetadata; - - if (NS_WARN_IF(!foundObjectStoreMetadata->mIndexes.Put(aMetadata.id(), - newMetadata, - fallible))) { - return false; - } - - dbMetadata->mNextIndexId++; - - nsRefPtr op = - new CreateIndexOp(this, aObjectStoreId, aMetadata); - - if (NS_WARN_IF(!op->Init(this))) { - op->Cleanup(); - return false; - } - - op->DispatchToTransactionThreadPool(); - - return true; -} - -bool -VersionChangeTransaction::RecvDeleteIndex(const int64_t& aObjectStoreId, - const int64_t& aIndexId) -{ - AssertIsOnBackgroundThread(); - - if (NS_WARN_IF(!aObjectStoreId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(!aIndexId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - const nsRefPtr dbMetadata = GetDatabase()->Metadata(); - MOZ_ASSERT(dbMetadata); - MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0); - MOZ_ASSERT(dbMetadata->mNextIndexId > 0); - - if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr foundObjectStoreMetadata = - GetMetadataForObjectStoreId(aObjectStoreId); - - if (NS_WARN_IF(!foundObjectStoreMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - nsRefPtr foundIndexMetadata = - GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId); - - if (NS_WARN_IF(!foundIndexMetadata)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - foundIndexMetadata->mDeleted = true; - - nsRefPtr op = new DeleteIndexOp(this, aIndexId); - - if (NS_WARN_IF(!op->Init(this))) { - op->Cleanup(); - return false; - } - - op->DispatchToTransactionThreadPool(); - - return true; -} - -PBackgroundIDBRequestParent* -VersionChangeTransaction::AllocPBackgroundIDBRequestParent( - const RequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - - return AllocRequest(aParams, IsSameProcessActor()); -} - -bool -VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor( - PBackgroundIDBRequestParent* aActor, - const RequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - - return StartRequest(aActor); -} - -bool -VersionChangeTransaction::DeallocPBackgroundIDBRequestParent( - PBackgroundIDBRequestParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - return DeallocRequest(aActor); -} - -PBackgroundIDBCursorParent* -VersionChangeTransaction::AllocPBackgroundIDBCursorParent( - const OpenCursorParams& aParams) -{ - AssertIsOnBackgroundThread(); - - return AllocCursor(aParams, IsSameProcessActor()); -} - -bool -VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor( - PBackgroundIDBCursorParent* aActor, - const OpenCursorParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); - - return StartCursor(aActor, aParams); -} - -bool -VersionChangeTransaction::DeallocPBackgroundIDBCursorParent( - PBackgroundIDBCursorParent* aActor) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aActor); - - return DeallocCursor(aActor); -} - -/******************************************************************************* - * Cursor - ******************************************************************************/ - -Cursor::Cursor(TransactionBase* aTransaction, - Type aType, - int64_t aObjectStoreId, - int64_t aIndexId, - Direction aDirection) - : mTransaction(aTransaction) - , mBackgroundParent(nullptr) - , mObjectStoreId(aObjectStoreId) - , mIndexId(aIndexId) - , mCurrentlyRunningOp(nullptr) - , mType(aType) - , mDirection(aDirection) - , mUniqueIndex(false) - , mActorDestroyed(false) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(aType != OpenCursorParams::T__None); - MOZ_ASSERT(aObjectStoreId); - MOZ_ASSERT_IF(aType == OpenCursorParams::TIndexOpenCursorParams || - aType == OpenCursorParams::TIndexOpenKeyCursorParams, - aIndexId); - - if (mType == OpenCursorParams::TObjectStoreOpenCursorParams || - mType == OpenCursorParams::TIndexOpenCursorParams) { - mFileManager = aTransaction->GetDatabase()->GetFileManager(); - MOZ_ASSERT(mFileManager); - - mBackgroundParent = aTransaction->GetBackgroundParent(); - MOZ_ASSERT(mBackgroundParent); - } - - if (aIndexId) { - MOZ_ASSERT(aType == OpenCursorParams::TIndexOpenCursorParams || - aType == OpenCursorParams::TIndexOpenKeyCursorParams); - - nsRefPtr objectStoreMetadata = - aTransaction->GetMetadataForObjectStoreId(aObjectStoreId); - MOZ_ASSERT(objectStoreMetadata); - - nsRefPtr indexMetadata = - aTransaction->GetMetadataForIndexId(objectStoreMetadata, aIndexId); - MOZ_ASSERT(indexMetadata); - - mUniqueIndex = indexMetadata->mCommonMetadata.unique(); - } - - static_assert(OpenCursorParams::T__None == 0 && - OpenCursorParams::T__Last == 4, - "Lots of code here assumes only four types of cursors!"); -} - -bool -Cursor::Start(const OpenCursorParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() == mType); - MOZ_ASSERT(!mActorDestroyed); - - if (NS_WARN_IF(mCurrentlyRunningOp)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - const OptionalKeyRange& optionalKeyRange = - mType == OpenCursorParams::TObjectStoreOpenCursorParams ? - aParams.get_ObjectStoreOpenCursorParams().optionalKeyRange() : - mType == OpenCursorParams::TObjectStoreOpenKeyCursorParams ? - aParams.get_ObjectStoreOpenKeyCursorParams().optionalKeyRange() : - mType == OpenCursorParams::TIndexOpenCursorParams ? - aParams.get_IndexOpenCursorParams().optionalKeyRange() : - aParams.get_IndexOpenKeyCursorParams().optionalKeyRange(); - - if (mTransaction->IsInvalidated()) { - return true; - } - - nsRefPtr openOp = new OpenOp(this, optionalKeyRange); - - if (NS_WARN_IF(!openOp->Init(mTransaction))) { - openOp->Cleanup(); - return false; - } - - openOp->DispatchToTransactionThreadPool(); - mCurrentlyRunningOp = openOp; - - return true; -} - -void -Cursor::SendResponseInternal(CursorResponse& aResponse, - const nsTArray& aFiles) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aResponse.type() != CursorResponse::T__None); - MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult, - NS_FAILED(aResponse.get_nsresult())); - MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult, - NS_ERROR_GET_MODULE(aResponse.get_nsresult()) == - NS_ERROR_MODULE_DOM_INDEXEDDB); - MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, mKey.IsUnset()); - MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, - mRangeKey.IsUnset()); - MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, - mObjectKey.IsUnset()); - MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult || - aResponse.type() == CursorResponse::Tvoid_t || - aResponse.type() == - CursorResponse::TObjectStoreKeyCursorResponse || - aResponse.type() == CursorResponse::TIndexKeyCursorResponse, - aFiles.IsEmpty()); - MOZ_ASSERT(!mActorDestroyed); - MOZ_ASSERT(mCurrentlyRunningOp); - - if (!aFiles.IsEmpty()) { - MOZ_ASSERT(aResponse.type() == CursorResponse::TObjectStoreCursorResponse || - aResponse.type() == CursorResponse::TIndexCursorResponse); - MOZ_ASSERT(mFileManager); - MOZ_ASSERT(mBackgroundParent); - - FallibleTArray actors; - FallibleTArray fileInfos; - nsresult rv = ConvertBlobsToActors(mBackgroundParent, - mFileManager, - aFiles, - actors, - fileInfos); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResponse = ClampResultCode(rv); - } else { - SerializedStructuredCloneReadInfo* serializedInfo = nullptr; - switch (aResponse.type()) { - case CursorResponse::TObjectStoreCursorResponse: - serializedInfo = - &aResponse.get_ObjectStoreCursorResponse().cloneInfo(); - break; - - case CursorResponse::TIndexCursorResponse: - serializedInfo = &aResponse.get_IndexCursorResponse().cloneInfo(); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - MOZ_ASSERT(serializedInfo); - MOZ_ASSERT(serializedInfo->blobsParent().IsEmpty()); - MOZ_ASSERT(serializedInfo->fileInfos().IsEmpty()); - - serializedInfo->blobsParent().SwapElements(actors); - serializedInfo->fileInfos().SwapElements(fileInfos); - } - } - - // Work around the deleted function by casting to the base class. - auto* base = static_cast(this); - if (!base->SendResponse(aResponse)) { - NS_WARNING("Failed to send response!"); - } - - mCurrentlyRunningOp = nullptr; -} - -void -Cursor::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; - - if (mCurrentlyRunningOp) { - mCurrentlyRunningOp->NoteActorDestroyed(); - } - - mBackgroundParent = nullptr; -} - -bool -Cursor::RecvDeleteMe() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(!mActorDestroyed); - - if (NS_WARN_IF(mCurrentlyRunningOp)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - return PBackgroundIDBCursorParent::Send__delete__(this); -} - -bool -Cursor::RecvContinue(const CursorRequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None); - MOZ_ASSERT(!mActorDestroyed); - - if (NS_WARN_IF(mCurrentlyRunningOp)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (NS_WARN_IF(mTransaction->mCommitOrAbortReceived)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - - if (aParams.type() == CursorRequestParams::TContinueParams) { - const Key& key = aParams.get_ContinueParams().key(); - if (!key.IsUnset()) { - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - if (NS_WARN_IF(key <= mKey)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - if (NS_WARN_IF(key >= mKey)) { - ASSERT_UNLESS_FUZZING(); - return false; - } - break; - - default: - MOZ_CRASH("Should never get here!"); - } - } - } - - if (mTransaction->IsInvalidated()) { - return true; - } - - nsRefPtr continueOp = new ContinueOp(this, aParams); - if (NS_WARN_IF(!continueOp->Init(mTransaction))) { - continueOp->Cleanup(); - return false; - } - - continueOp->DispatchToTransactionThreadPool(); - mCurrentlyRunningOp = continueOp; - - return true; -} - -/******************************************************************************* - * FileManager - ******************************************************************************/ - -FileManager::FileManager(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - StoragePrivilege aPrivilege, - const nsAString& aDatabaseName) - : mPersistenceType(aPersistenceType) - , mGroup(aGroup) - , mOrigin(aOrigin) - , mPrivilege(aPrivilege) - , mDatabaseName(aDatabaseName) - , mLastFileId(0) - , mInvalidated(false) -{ } - -FileManager::~FileManager() -{ } - -nsresult -FileManager::Init(nsIFile* aDirectory, - mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aDirectory); - MOZ_ASSERT(aConnection); - - bool exists; - nsresult rv = aDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - bool isDirectory; - rv = aDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!isDirectory)) { - return NS_ERROR_FAILURE; - } - } else { - rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = aDirectory->GetPath(mDirectoryPath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr journalDirectory; - rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - NS_ConvertASCIItoUTF16 dirName(NS_LITERAL_CSTRING(kJournalDirectoryName)); - rv = journalDirectory->Append(dirName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = journalDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - bool isDirectory; - rv = journalDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!isDirectory)) { - return NS_ERROR_FAILURE; - } - } - - rv = journalDirectory->GetPath(mJournalDirectoryPath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr stmt; - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, refcount " - "FROM file" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasResult; - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - int64_t id; - rv = stmt->GetInt64(0, &id); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int32_t refcount; - rv = stmt->GetInt32(1, &refcount); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(refcount > 0); - - nsRefPtr fileInfo = FileInfo::Create(this, id); - fileInfo->mDBRefCnt = static_cast(refcount); - - mFileInfos.Put(id, fileInfo); - - mLastFileId = std::max(id, mLastFileId); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -FileManager::Invalidate() -{ - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static PLDHashOperator - CopyToTArray(const uint64_t& aKey, FileInfo* aValue, void* aUserArg) - { - MOZ_ASSERT(aValue); - - auto* array = static_cast*>(aUserArg); - MOZ_ASSERT(array); - - MOZ_ALWAYS_TRUE(array->AppendElement(aValue)); - - return PL_DHASH_NEXT; - } - }; - - if (IndexedDatabaseManager::IsClosed()) { - MOZ_ASSERT(false, "Shouldn't be called after shutdown!"); - return NS_ERROR_UNEXPECTED; - } - - FallibleTArray fileInfos; - { - MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - - MOZ_ASSERT(!mInvalidated); - mInvalidated = true; - - if (NS_WARN_IF(!fileInfos.SetCapacity(mFileInfos.Count()))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - mFileInfos.EnumerateRead(Helper::CopyToTArray, &fileInfos); - } - - for (uint32_t count = fileInfos.Length(), index = 0; index < count; index++) { - FileInfo* fileInfo = fileInfos[index]; - MOZ_ASSERT(fileInfo); - - fileInfo->ClearDBRefs(); - } - - return NS_OK; -} - -already_AddRefed -FileManager::GetDirectory() -{ - return GetFileForPath(mDirectoryPath); -} - -already_AddRefed -FileManager::GetJournalDirectory() -{ - return GetFileForPath(mJournalDirectoryPath); -} - -already_AddRefed -FileManager::EnsureJournalDirectory() -{ - // This can happen on the IO or on a transaction thread. - MOZ_ASSERT(!NS_IsMainThread()); - - nsCOMPtr journalDirectory = GetFileForPath(mJournalDirectoryPath); - if (NS_WARN_IF(!journalDirectory)) { - return nullptr; - } - - bool exists; - nsresult rv = journalDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - if (exists) { - bool isDirectory; - rv = journalDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - if (NS_WARN_IF(!isDirectory)) { - return nullptr; - } - } else { - rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - } - - return journalDirectory.forget(); -} - -already_AddRefed -FileManager::GetFileInfo(int64_t aId) -{ - if (IndexedDatabaseManager::IsClosed()) { - MOZ_ASSERT(false, "Shouldn't be called after shutdown!"); - return nullptr; - } - - FileInfo* fileInfo; - { - MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - fileInfo = mFileInfos.Get(aId); - } - - nsRefPtr result = fileInfo; - return result.forget(); -} - -already_AddRefed -FileManager::GetNewFileInfo() -{ - MOZ_ASSERT(!IndexedDatabaseManager::IsClosed()); - - FileInfo* fileInfo; - { - MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - - int64_t id = mLastFileId + 1; - - fileInfo = FileInfo::Create(this, id); - - mFileInfos.Put(id, fileInfo); - - mLastFileId = id; - } - - nsRefPtr result = fileInfo; - return result.forget(); -} - -// static -already_AddRefed -FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId) -{ - MOZ_ASSERT(aDirectory); - MOZ_ASSERT(aId > 0); - - nsAutoString id; - id.AppendInt(aId); - - nsCOMPtr file; - nsresult rv = aDirectory->Clone(getter_AddRefs(file)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - rv = file->Append(id); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - return file.forget(); -} - -// static -nsresult -FileManager::InitDirectory(nsIFile* aDirectory, - nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aDirectory); - MOZ_ASSERT(aDatabaseFile); - - bool exists; - nsresult rv = aDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!exists) { - return NS_OK; - } - - bool isDirectory; - rv = aDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!isDirectory)) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr journalDirectory; - rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - NS_ConvertASCIItoUTF16 dirName(NS_LITERAL_CSTRING(kJournalDirectoryName)); - rv = journalDirectory->Append(dirName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = journalDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - rv = journalDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!isDirectory)) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr entries; - rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasElements; - rv = entries->HasMoreElements(&hasElements); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasElements) { - nsCOMPtr connection; - rv = CreateDatabaseConnection(aDatabaseFile, - aDirectory, - NullString(), - aPersistenceType, - aGroup, - aOrigin, - getter_AddRefs(connection)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mozStorageTransaction transaction(connection, false); - - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE VIRTUAL TABLE fs USING filesystem;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr stmt; - rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name, (name IN (SELECT id FROM file)) " - "FROM fs " - "WHERE path = :path" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsString path; - rv = journalDirectory->GetPath(path); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasResult; - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - nsString name; - rv = stmt->GetString(0, name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int32_t flag = stmt->AsInt32(1); - - if (!flag) { - nsCOMPtr file; - rv = aDirectory->Clone(getter_AddRefs(file)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = file->Append(name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_FAILED(file->Remove(false))) { - NS_WARNING("Failed to remove orphaned file!"); - } - } - - nsCOMPtr journalFile; - rv = journalDirectory->Clone(getter_AddRefs(journalFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = journalFile->Append(name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_FAILED(journalFile->Remove(false))) { - NS_WARNING("Failed to remove journal file!"); - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE fs;" - )); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - transaction.Commit(); - } - } - - return NS_OK; -} - -// static -nsresult -FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aDirectory); - MOZ_ASSERT(aUsage); - - bool exists; - nsresult rv = aDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!exists) { - *aUsage = 0; - return NS_OK; - } - - nsCOMPtr entries; - rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - uint64_t usage = 0; - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr file = do_QueryInterface(entry); - MOZ_ASSERT(file); - - nsString leafName; - rv = file->GetLeafName(leafName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (leafName.EqualsLiteral(kJournalDirectoryName)) { - continue; - } - - int64_t fileSize; - rv = file->GetFileSize(&fileSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - quota::IncrementUsage(&usage, uint64_t(fileSize)); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - *aUsage = usage; - return NS_OK; -} - -/******************************************************************************* - * QuotaClient - ******************************************************************************/ - -QuotaClient* QuotaClient::sInstance = nullptr; - -QuotaClient::QuotaClient() - : mShutdownRequested(false) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!sInstance, "We expect this to be a singleton!"); - - sInstance = this; -} - -QuotaClient::~QuotaClient() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!"); - - sInstance = nullptr; -} - -void -QuotaClient::NoteBackgroundThread(nsIEventTarget* aBackgroundThread) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aBackgroundThread); - MOZ_ASSERT(!mShutdownRequested); - - mBackgroundThread = aBackgroundThread; -} - -void -QuotaClient::NoteNewTransactionThreadPool() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(gCurrentTransactionThreadPool); - MOZ_ASSERT(!mDyingTransactionThreadPools.Contains( - gCurrentTransactionThreadPool)); - - if (!mDyingTransactionThreadPools.IsEmpty()) { - for (uint32_t index = mDyingTransactionThreadPools.Length(); - index > 0; - index--) { - const uint32_t thisIndex = index - 1; - - if (mDyingTransactionThreadPools[thisIndex]->HasCompletedShutdown()) { - mDyingTransactionThreadPools.RemoveElementAt(thisIndex); - } - } - } -} - -void -QuotaClient::NoteDyingTransactionThreadPool() -{ - AssertIsOnBackgroundThread(); - - if (gCurrentTransactionThreadPool) { - if (!gCurrentTransactionThreadPool->HasCompletedShutdown() && - !mDyingTransactionThreadPools.Contains(gCurrentTransactionThreadPool)) { - mDyingTransactionThreadPools.AppendElement(gCurrentTransactionThreadPool); - } - - gCurrentTransactionThreadPool = nullptr; - } -} - -mozilla::dom::quota::Client::Type -QuotaClient::GetType() -{ - return QuotaClient::IDB; -} - -nsresult -QuotaClient::InitOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - UsageInfo* aUsageInfo) -{ - AssertIsOnIOThread(); - - nsCOMPtr directory; - nsresult rv = - GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // We need to see if there are any files in the directory already. If they - // are database files then we need to cleanup stored files (if it's needed) - // and also get the usage. - - nsAutoTArray subdirsToProcess; - nsAutoTArray , 20> unknownFiles; - nsTHashtable validSubdirs(20); - - nsCOMPtr entries; - rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && - hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr file = do_QueryInterface(entry); - MOZ_ASSERT(file); - - nsString leafName; - rv = file->GetLeafName(leafName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { - continue; - } - - if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { - continue; - } - - bool isDirectory; - rv = file->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (isDirectory) { - if (!validSubdirs.GetEntry(leafName)) { - subdirsToProcess.AppendElement(leafName); - } - continue; - } - - nsString dbBaseFilename; - if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) { - unknownFiles.AppendElement(file); - continue; - } - - nsCOMPtr fmDirectory; - rv = directory->Clone(getter_AddRefs(fmDirectory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = fmDirectory->Append(dbBaseFilename); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup, - aOrigin); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (aUsageInfo) { - int64_t fileSize; - rv = file->GetFileSize(&fileSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(fileSize >= 0); - - aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); - - uint64_t usage; - rv = FileManager::GetUsage(fmDirectory, &usage); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - aUsageInfo->AppendToFileUsage(usage); - } - - validSubdirs.PutEntry(dbBaseFilename); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) { - const nsString& subdir = subdirsToProcess[i]; - if (NS_WARN_IF(!validSubdirs.GetEntry(subdir))) { - return NS_ERROR_UNEXPECTED; - } - } - - for (uint32_t i = 0; i < unknownFiles.Length(); i++) { - nsCOMPtr& unknownFile = unknownFiles[i]; - - // Some temporary SQLite files could disappear, so we have to check if the - // unknown file still exists. - bool exists; - rv = unknownFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - nsString leafName; - unknownFile->GetLeafName(leafName); - - // The journal file may exists even after db has been correctly opened. - if (NS_WARN_IF(!StringEndsWith(leafName, - NS_LITERAL_STRING(".sqlite-journal")))) { - return NS_ERROR_UNEXPECTED; - } - } - } - - return NS_OK; -} - -nsresult -QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - UsageInfo* aUsageInfo) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aUsageInfo); - - nsCOMPtr directory; - nsresult rv = - GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -void -QuotaClient::OnOriginClearCompleted( - PersistenceType aPersistenceType, - const OriginOrPatternString& aOriginOrPattern) -{ - AssertIsOnIOThread(); - - if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) { - mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern); - } -} - -void -QuotaClient::ReleaseIOThreadObjects() -{ - AssertIsOnIOThread(); - - if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) { - mgr->InvalidateAllFileManagers(); - } -} - -bool -QuotaClient::IsFileServiceUtilized() -{ - MOZ_ASSERT(NS_IsMainThread()); - - return true; -} - -bool -QuotaClient::IsTransactionServiceActivated() -{ - MOZ_ASSERT(NS_IsMainThread()); - - return true; -} - -void -QuotaClient::WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aStorages.IsEmpty()); - MOZ_ASSERT(aCallback); - - nsCOMPtr backgroundThread; - nsTArray databaseIds; - - for (uint32_t count = aStorages.Length(), index = 0; index < count; index++) { - nsIOfflineStorage* storage = aStorages[index]; - MOZ_ASSERT(storage); - MOZ_ASSERT(storage->GetClient() == this); - - const nsACString& databaseId = storage->Id(); - - if (!databaseIds.Contains(databaseId)) { - databaseIds.AppendElement(databaseId); - - if (!backgroundThread) { - backgroundThread = - static_cast(storage)->OwningThread(); - MOZ_ASSERT(backgroundThread); - } -#ifdef DEBUG - else { - MOZ_ASSERT(backgroundThread == - static_cast(storage)-> - OwningThread()); - } -#endif - } - } - - if (databaseIds.IsEmpty()) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(aCallback))); - return; - } - - MOZ_ASSERT(backgroundThread); - - nsCOMPtr runnable = - new WaitForTransactionsRunnable(this, databaseIds, aCallback); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - backgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))); -} - -void -QuotaClient::AbortTransactionsForStorage(nsIOfflineStorage* aStorage) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aStorage); - MOZ_ASSERT(aStorage->GetClient() == this); - MOZ_ASSERT(aStorage->IsClosed()); - - // Nothing to do here, calling DatabaseOfflineStorage::Close() should have - // aborted any transactions already. -} - -bool -QuotaClient::HasTransactionsForStorage(nsIOfflineStorage* aStorage) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aStorage); - MOZ_ASSERT(aStorage->GetClient() == this); - - return static_cast(aStorage)->HasOpenTransactions(); -} - -void -QuotaClient::ShutdownTransactionService() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mShutdownRunnable); - MOZ_ASSERT(!mShutdownRequested); - - mShutdownRequested = true; - - if (mBackgroundThread) { - nsRefPtr runnable = - new ShutdownTransactionThreadPoolRunnable(this); - - if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { - // This can happen if the thread has shut down already. - return; - } - - nsIThread* currentThread = NS_GetCurrentThread(); - MOZ_ASSERT(currentThread); - - mShutdownRunnable.swap(runnable); - - while (mShutdownRunnable) { - MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); - } - } -} - -nsresult -QuotaClient::GetDirectory(PersistenceType aPersistenceType, - const nsACString& aOrigin, nsIFile** aDirectory) -{ - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - nsCOMPtr directory; - nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin, - getter_AddRefs(directory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(directory); - - rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - directory.forget(aDirectory); - return NS_OK; -} - -nsresult -QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory, - UsageInfo* aUsageInfo, - bool aDatabaseFiles) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aDirectory); - MOZ_ASSERT(aUsageInfo); - - nsCOMPtr entries; - nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!entries) { - return NS_OK; - } - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && - hasMore && - !aUsageInfo->Canceled()) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr file = do_QueryInterface(entry); - MOZ_ASSERT(file); - - bool isDirectory; - rv = file->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (isDirectory) { - if (aDatabaseFiles) { - rv = GetUsageForDirectoryInternal(file, aUsageInfo, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - nsString leafName; - rv = file->GetLeafName(leafName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!leafName.EqualsLiteral(kJournalDirectoryName)) { - NS_WARNING("Unknown directory found!"); - } - } - - continue; - } - - int64_t fileSize; - rv = file->GetFileSize(&fileSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(fileSize >= 0); - - if (aDatabaseFiles) { - aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); - } else { - aUsageInfo->AppendToFileUsage(uint64_t(fileSize)); - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - - -void -QuotaClient:: -WaitForTransactionsRunnable::MaybeWait() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mState == State_Initial); - MOZ_ASSERT(mQuotaClient); - - nsRefPtr threadPool = gCurrentTransactionThreadPool; - - if (threadPool) { - mState = State_WaitingForTransactions; - - threadPool->WaitForDatabasesToComplete(mDatabaseIds, this); - - MOZ_ASSERT(mDatabaseIds.IsEmpty()); - return; - } - - mDatabaseIds.Clear(); - - SendToMainThread(); -} - -void -QuotaClient:: -WaitForTransactionsRunnable::SendToMainThread() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mState == State_Initial || mState == State_WaitingForTransactions); - MOZ_ASSERT(mDatabaseIds.IsEmpty()); - - mState = State_CallingCallback; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); -} - -void -QuotaClient:: -WaitForTransactionsRunnable::CallCallback() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_CallingCallback); - MOZ_ASSERT(mDatabaseIds.IsEmpty()); - - nsRefPtr quotaClient; - mQuotaClient.swap(quotaClient); - - nsCOMPtr callback; - mCallback.swap(callback); - - callback->Run(); - - mState = State_Complete; -} - -NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::WaitForTransactionsRunnable, - nsRunnable) - -NS_IMETHODIMP -QuotaClient:: -WaitForTransactionsRunnable::Run() -{ - MOZ_ASSERT(mState != State_Complete); - MOZ_ASSERT(mCallback); - - switch (mState) { - case State_Initial: - MaybeWait(); - break; - - case State_WaitingForTransactions: - SendToMainThread(); - break; - - case State_CallingCallback: - CallCallback(); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - return NS_OK; -} - -NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::ShutdownTransactionThreadPoolRunnable, - nsRunnable) - -NS_IMETHODIMP -QuotaClient:: -ShutdownTransactionThreadPoolRunnable::Run() -{ - if (NS_IsMainThread()) { - MOZ_ASSERT(mHasRequestedShutDown); - MOZ_ASSERT(QuotaClient::GetInstance() == mQuotaClient); - MOZ_ASSERT(mQuotaClient->mShutdownRunnable == this); - - mQuotaClient->mShutdownRunnable = nullptr; - mQuotaClient = nullptr; - - return NS_OK; - } - - AssertIsOnBackgroundThread(); - - if (!mHasRequestedShutDown) { - mHasRequestedShutDown = true; - - mQuotaClient->NoteDyingTransactionThreadPool(); - MOZ_ASSERT(!gCurrentTransactionThreadPool); - - while (!mQuotaClient->mDyingTransactionThreadPools.IsEmpty()) { - nsRefPtr threadPool; - mQuotaClient->mDyingTransactionThreadPools[0].swap(threadPool); - mQuotaClient->mDyingTransactionThreadPools.RemoveElementAt(0); - - if (!threadPool->HasCompletedShutdown()) { - threadPool->ShutdownAndSpin(); - - MOZ_ASSERT(threadPool->HasCompletedShutdown()); - } - } - - MOZ_ASSERT(!gCurrentTransactionThreadPool); - MOZ_ASSERT(mQuotaClient->mDyingTransactionThreadPools.IsEmpty()); - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); - - return NS_OK; -} - -/******************************************************************************* - * DatabaseOfflineStorage - ******************************************************************************/ - -DatabaseOfflineStorage::DatabaseOfflineStorage( - QuotaClient* aQuotaClient, - const OptionalWindowId& aOptionalWindowId, - const OptionalWindowId& aOptionalContentParentId, - const nsACString& aGroup, - const nsACString& aOrigin, - const nsACString& aId, - PersistenceType aPersistenceType, - nsIEventTarget* aOwningThread) - : mStrongQuotaClient(aQuotaClient) - , mWeakQuotaClient(aQuotaClient) - , mDatabase(nullptr) - , mOptionalWindowId(aOptionalWindowId) - , mOptionalContentParentId(aOptionalContentParentId) - , mOrigin(aOrigin) - , mId(aId) - , mOwningThread(aOwningThread) - , mTransactionCount(0) - , mClosedOnMainThread(false) - , mClosedOnOwningThread(false) - , mInvalidatedOnMainThread(false) - , mInvalidatedOnOwningThread(false) - , mRegisteredWithQuotaManager(false) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aQuotaClient); - MOZ_ASSERT(aOptionalWindowId.type() != OptionalWindowId::T__None); - MOZ_ASSERT_IF(aOptionalWindowId.type() == OptionalWindowId::Tuint64_t, - aOptionalContentParentId.type() == OptionalWindowId::Tvoid_t); - MOZ_ASSERT(aOptionalContentParentId.type() != OptionalWindowId::T__None); - MOZ_ASSERT_IF(aOptionalContentParentId.type() == OptionalWindowId::Tuint64_t, - aOptionalWindowId.type() == OptionalWindowId::Tvoid_t); - MOZ_ASSERT(aOwningThread); - - DebugOnly current; - MOZ_ASSERT(NS_SUCCEEDED(aOwningThread->IsOnCurrentThread(¤t))); - MOZ_ASSERT(!current); - - mGroup = aGroup; - mPersistenceType = aPersistenceType; -} - -// static -void -DatabaseOfflineStorage::UnregisterOnOwningThread( - already_AddRefed aOfflineStorage) -{ - AssertIsOnBackgroundThread(); - - nsRefPtr offlineStorage = Move(aOfflineStorage); - MOZ_ASSERT(offlineStorage); - MOZ_ASSERT(offlineStorage->mClosedOnOwningThread); - - offlineStorage->mDatabase = nullptr; - - nsCOMPtr runnable = - NS_NewRunnableMethod(offlineStorage.get(), - &DatabaseOfflineStorage::UnregisterOnMainThread); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); -} - -void -DatabaseOfflineStorage::CloseOnOwningThread() -{ - AssertIsOnBackgroundThread(); - - if (mClosedOnOwningThread) { - return; - } - - mClosedOnOwningThread = true; - - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &DatabaseOfflineStorage::CloseOnMainThread); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); -} - -void -DatabaseOfflineStorage::CloseOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mClosedOnMainThread) { - return; - } - - mClosedOnMainThread = true; - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - quotaManager->OnStorageClosed(this); -} - -void -DatabaseOfflineStorage::InvalidateOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mInvalidatedOnMainThread) { - return; - } - - mInvalidatedOnMainThread = true; - - nsCOMPtr runnable = - NS_NewRunnableMethod(this, - &DatabaseOfflineStorage::InvalidateOnOwningThread); - MOZ_ASSERT(runnable); - - nsCOMPtr owningThread = mOwningThread; - MOZ_ASSERT(owningThread); - - CloseOnMainThread(); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(owningThread->Dispatch(runnable, - NS_DISPATCH_NORMAL))); -} - -void -DatabaseOfflineStorage::InvalidateOnOwningThread() -{ - AssertIsOnBackgroundThread(); - - if (mInvalidatedOnOwningThread) { - return; - } - - mInvalidatedOnOwningThread = true; - - if (nsRefPtr database = mDatabase) { - mDatabase = nullptr; - - database->Invalidate(); - } -} - -void -DatabaseOfflineStorage::UnregisterOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mOwningThread); - MOZ_ASSERT(mRegisteredWithQuotaManager); - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - quotaManager->UnregisterStorage(this); - mRegisteredWithQuotaManager = false; - - mStrongQuotaClient = nullptr; - - mOwningThread = nullptr; -} - -NS_IMPL_ISUPPORTS(DatabaseOfflineStorage, nsIOfflineStorage) - -NS_IMETHODIMP_(const nsACString&) -DatabaseOfflineStorage::Id() -{ - return mId; -} - -NS_IMETHODIMP_(Client*) -DatabaseOfflineStorage::GetClient() -{ - MOZ_ASSERT(NS_IsMainThread()); - - return mWeakQuotaClient; -} - -NS_IMETHODIMP_(bool) -DatabaseOfflineStorage::IsOwnedByWindow(nsPIDOMWindow* aOwner) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aOwner); - MOZ_ASSERT(aOwner->IsInnerWindow()); - - return mOptionalWindowId.type() == OptionalWindowId::Tuint64_t && - mOptionalWindowId.get_uint64_t() == aOwner->WindowID(); -} - -NS_IMETHODIMP_(bool) -DatabaseOfflineStorage::IsOwnedByProcess(ContentParent* aOwner) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aOwner); - - return mOptionalContentParentId.type() == OptionalWindowId::Tuint64_t && - mOptionalContentParentId.get_uint64_t() == aOwner->ChildID(); -} - -NS_IMETHODIMP_(const nsACString&) -DatabaseOfflineStorage::Origin() -{ - return mOrigin; -} - -NS_IMETHODIMP_(nsresult) -DatabaseOfflineStorage::Close() -{ - MOZ_ASSERT(NS_IsMainThread()); - - InvalidateOnMainThread(); - return NS_OK; -} - -NS_IMETHODIMP_(bool) -DatabaseOfflineStorage::IsClosed() -{ - MOZ_ASSERT(NS_IsMainThread()); - - return mClosedOnMainThread; -} - -NS_IMETHODIMP_(void) -DatabaseOfflineStorage::Invalidate() -{ - MOZ_ASSERT(NS_IsMainThread()); - - InvalidateOnMainThread(); -} - -/******************************************************************************* - * Local class implementations - ******************************************************************************/ - -NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction) -NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction) - -uint64_t DatabaseOperationBase::sNextSerialNumber = 0; - -// static -void -DatabaseOperationBase::GetBindingClauseForKeyRange( - const SerializedKeyRange& aKeyRange, - const nsACString& aKeyColumnName, - nsAutoCString& aBindingClause) -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(!aKeyColumnName.IsEmpty()); - - NS_NAMED_LITERAL_CSTRING(andStr, " AND "); - NS_NAMED_LITERAL_CSTRING(spacecolon, " :"); - NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); - - if (aKeyRange.isOnly()) { - // Both keys equal. - aBindingClause = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + - spacecolon + lowerKey; - return; - } - - aBindingClause.Truncate(); - - if (!aKeyRange.lower().IsUnset()) { - // Lower key is set. - aBindingClause.Append(andStr + aKeyColumnName); - aBindingClause.AppendLiteral(" >"); - if (!aKeyRange.lowerOpen()) { - aBindingClause.AppendLiteral("="); - } - aBindingClause.Append(spacecolon + lowerKey); - } - - if (!aKeyRange.upper().IsUnset()) { - // Upper key is set. - aBindingClause.Append(andStr + aKeyColumnName); - aBindingClause.AppendLiteral(" <"); - if (!aKeyRange.upperOpen()) { - aBindingClause.AppendLiteral("="); - } - aBindingClause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key")); - } - - MOZ_ASSERT(!aBindingClause.IsEmpty()); -} - -// static -uint64_t -DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble) -{ - // This is a duplicate of the js engine's byte munging in StructuredClone.cpp - union { - double d; - uint64_t u; - } pun; - pun.d = aDouble; - return pun.u; -} - -// static -nsresult -DatabaseOperationBase::GetStructuredCloneReadInfoFromStatement( - mozIStorageStatement* aStatement, - uint32_t aDataIndex, - uint32_t aFileIdsIndex, - FileManager* aFileManager, - StructuredCloneReadInfo* aInfo) -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(aStatement); - MOZ_ASSERT(aFileManager); - - PROFILER_LABEL("IndexedDB", - "DatabaseOperationBase::" - "GetStructuredCloneReadInfoFromStatement", - js::ProfileEntry::Category::STORAGE); - -#ifdef DEBUG - { - int32_t type; - MOZ_ASSERT(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type))); - MOZ_ASSERT(type == mozIStorageStatement::VALUE_TYPE_BLOB); - } -#endif - - const uint8_t* blobData; - uint32_t blobDataLength; - nsresult rv = - aStatement->GetSharedBlob(aDataIndex, &blobDataLength, &blobData); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - const char* compressed = reinterpret_cast(blobData); - size_t compressedLength = size_t(blobDataLength); - - size_t uncompressedLength; - if (NS_WARN_IF(!snappy::GetUncompressedLength(compressed, compressedLength, - &uncompressedLength))) { - return NS_ERROR_FILE_CORRUPTED; - } - - FallibleTArray uncompressed; - if (NS_WARN_IF(!uncompressed.SetLength(uncompressedLength))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - char* uncompressedBuffer = reinterpret_cast(uncompressed.Elements()); - - if (NS_WARN_IF(!snappy::RawUncompress(compressed, compressedLength, - uncompressedBuffer))) { - return NS_ERROR_FILE_CORRUPTED; - } - - aInfo->mData.SwapElements(uncompressed); - - bool isNull; - rv = aStatement->GetIsNull(aFileIdsIndex, &isNull); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!isNull) { - nsString ids; - rv = aStatement->GetString(aFileIdsIndex, ids); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoTArray array; - rv = ConvertFileIdsToArray(ids, array); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - for (uint32_t count = array.Length(), index = 0; index < count; index++) { - MOZ_ASSERT(array[index] > 0); - - nsRefPtr fileInfo = aFileManager->GetFileInfo(array[index]); - MOZ_ASSERT(fileInfo); - - StructuredCloneFile* file = aInfo->mFiles.AppendElement(); - file->mFileInfo.swap(fileInfo); - } - } - - return NS_OK; -} - -// static -nsresult -DatabaseOperationBase::BindKeyRangeToStatement( - const SerializedKeyRange& aKeyRange, - mozIStorageStatement* aStatement) -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(aStatement); - - NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); - - if (aKeyRange.isOnly()) { - return aKeyRange.lower().BindToStatement(aStatement, lowerKey); - } - - nsresult rv; - - if (!aKeyRange.lower().IsUnset()) { - rv = aKeyRange.lower().BindToStatement(aStatement, lowerKey); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (!aKeyRange.upper().IsUnset()) { - rv = aKeyRange.upper().BindToStatement(aStatement, - NS_LITERAL_CSTRING("upper_key")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - return NS_OK; -} - -// static -void -DatabaseOperationBase::AppendConditionClause(const nsACString& aColumnName, - const nsACString& aArgName, - bool aLessThan, - bool aEquals, - nsAutoCString& aResult) -{ - aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName + - NS_LITERAL_CSTRING(" "); - - if (aLessThan) { - aResult.Append('<'); - } - else { - aResult.Append('>'); - } - - if (aEquals) { - aResult.Append('='); - } - - aResult += NS_LITERAL_CSTRING(" :") + aArgName; -} - -// static -nsresult -DatabaseOperationBase::UpdateIndexes( - TransactionBase* aTransaction, - const UniqueIndexTable& aUniqueIndexTable, - const Key& aObjectStoreKey, - bool aOverwrite, - int64_t aObjectDataId, - const nsTArray& aUpdateInfoArray) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(!aObjectStoreKey.IsUnset()); - - PROFILER_LABEL("IndexedDB", - "DatabaseOperationBase::UpdateIndexes", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); - - if (aOverwrite) { - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement( - "DELETE FROM unique_index_data " - "WHERE object_data_id = :object_data_id; " - "DELETE FROM index_data " - "WHERE object_data_id = :object_data_id", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - // Avoid lots of hash lookups for objectStores with lots of indexes by lazily - // holding the necessary statements on the stack outside the loop. - TransactionBase::CachedStatement insertUniqueStmt; - TransactionBase::CachedStatement insertStmt; - - for (uint32_t idxCount = aUpdateInfoArray.Length(), idxIndex = 0; - idxIndex < idxCount; - idxIndex++) { - const IndexUpdateInfo& updateInfo = aUpdateInfoArray[idxIndex]; - - bool unique; - MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(updateInfo.indexId(), &unique)); - - TransactionBase::CachedStatement& stmt = - unique ? insertUniqueStmt : insertStmt; - - if (stmt) { - stmt.Reset(); - } else if (unique) { - rv = aTransaction->GetCachedStatement( - "INSERT INTO unique_index_data " - "(index_id, object_data_id, object_data_key, value) " - "VALUES (:index_id, :object_data_id, :object_data_key, :value)", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - rv = aTransaction->GetCachedStatement( - "INSERT OR IGNORE INTO index_data (" - "index_id, object_data_id, object_data_key, value) " - "VALUES (:index_id, :object_data_id, :object_data_key, :value)", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - updateInfo.indexId()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = aObjectStoreKey.BindToStatement(stmt, - NS_LITERAL_CSTRING("object_data_key")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = updateInfo.value().BindToStatement(stmt, NS_LITERAL_CSTRING("value")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (rv == NS_ERROR_STORAGE_CONSTRAINT && unique) { - // If we're inserting multiple entries for the same unique index, then - // we might have failed to insert due to colliding with another entry for - // the same index in which case we should ignore it. - for (int32_t index = int32_t(idxIndex) - 1; - index >= 0 && - aUpdateInfoArray[index].indexId() == updateInfo.indexId(); - --index) { - if (updateInfo.value() == aUpdateInfoArray[index].value()) { - // We found a key with the same value for the same index. So we - // must have had a collision with a value we just inserted. - rv = NS_OK; - break; - } - } - } - - if (NS_FAILED(rv)) { - return rv; - } - } - - return NS_OK; -} - -NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase, - nsRunnable, - mozIStorageProgressHandler) - -NS_IMETHODIMP -DatabaseOperationBase::OnProgress(mozIStorageConnection* aConnection, - bool* _retval) -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(aConnection); - MOZ_ASSERT(_retval); - - // This is intentionally racy. - *_retval = !OperationMayProceed(); - return NS_OK; -} - -DatabaseOperationBase:: -AutoSetProgressHandler::~AutoSetProgressHandler() -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT_IF(mConnection, mDEBUGDatabaseOp); - - if (mConnection) { - nsCOMPtr oldHandler; - nsresult rv = - mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)); - if (NS_SUCCEEDED(rv)) { - MOZ_ASSERT(SameCOMIdentity(oldHandler, - static_cast(mDEBUGDatabaseOp))); - } else { - NS_WARNING("Failed to remove progress handler!"); - } - } -} - -nsresult -DatabaseOperationBase:: -AutoSetProgressHandler::Register( - DatabaseOperationBase* aDatabaseOp, - const nsCOMPtr& aConnection) -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(aDatabaseOp); - MOZ_ASSERT(aConnection); - MOZ_ASSERT(!mConnection); - MOZ_ASSERT(!mDEBUGDatabaseOp); - - nsCOMPtr oldProgressHandler; - - nsresult rv = - aConnection->SetProgressHandler(kStorageProgressGranularity, aDatabaseOp, - getter_AddRefs(oldProgressHandler)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!oldProgressHandler); - - mConnection = aConnection; - mDEBUGDatabaseOp = aDatabaseOp; - - return NS_OK; -} - -nsresult -FactoryOp::Open() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_Initial); - - // Swap this to the stack now to ensure that we release it on this thread. - nsRefPtr contentParent; - mContentParent.swap(contentParent); - - if (!OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - PermissionRequestBase::PermissionValue permission; - nsresult rv = CheckPermission(contentParent, &permission); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || - permission == PermissionRequestBase::kPermissionDenied || - permission == PermissionRequestBase::kPermissionPrompt); - - if (permission == PermissionRequestBase::kPermissionDenied) { - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } - - // This has to be started on the main thread currently. - if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - const DatabaseMetadata& metadata = mCommonParams.metadata(); - - QuotaManager::GetStorageId(metadata.persistenceType(), - mOrigin, - Client::IDB, - metadata.name(), - mDatabaseId); - - if (permission == PermissionRequestBase::kPermissionPrompt) { - mState = State_PermissionChallenge; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, - NS_DISPATCH_NORMAL))); - return NS_OK; - } - - MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed); - - rv = FinishOpen(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -FactoryOp::ChallengePermission() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_PermissionChallenge); - - const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); - MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); - - if (NS_WARN_IF(!SendPermissionChallenge(principalInfo))) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -nsresult -FactoryOp::RetryCheckPermission() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_PermissionRetry); - MOZ_ASSERT(mCommonParams.principalInfo().type() == - PrincipalInfo::TContentPrincipalInfo); - - // Swap this to the stack now to ensure that we release it on this thread. - nsRefPtr contentParent; - mContentParent.swap(contentParent); - - if (!OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - PermissionRequestBase::PermissionValue permission; - nsresult rv = CheckPermission(contentParent, &permission); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || - permission == PermissionRequestBase::kPermissionDenied || - permission == PermissionRequestBase::kPermissionPrompt); - - if (permission == PermissionRequestBase::kPermissionDenied || - permission == PermissionRequestBase::kPermissionPrompt) { - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } - - MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed); - - rv = FinishOpen(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -FactoryOp::SendToIOThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_OpenPending); - - if (!OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - QuotaManager* quotaManager = QuotaManager::Get(); - if (NS_WARN_IF(!quotaManager)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - // Must set this before dispatching otherwise we will race with the IO thread. - mState = State_DatabaseWorkOpen; - - nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return NS_OK; -} - -void -FactoryOp::WaitForTransactions() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_BeginVersionChange || - mState == State_WaitingForOtherDatabasesToClose); - MOZ_ASSERT(!mDatabaseId.IsEmpty()); - MOZ_ASSERT(!IsActorDestroyed()); - - nsTArray databaseIds; - databaseIds.AppendElement(mDatabaseId); - - TransactionThreadPool* threadPool = mFactory->GetTransactionThreadPool(); - MOZ_ASSERT(threadPool); - - // WaitForDatabasesToComplete() will run this op immediately if there are no - // transactions blocking it, so be sure to set the next state here before - // calling it. - mState = State_WaitingForTransactionsToComplete; - - threadPool->WaitForDatabasesToComplete(databaseIds, this); - return; -} - -void -FactoryOp::FinishSendResults() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_SendingResults); - MOZ_ASSERT(mFactory); - - // Make sure to release the factory on this thread. - nsRefPtr factory; - mFactory.swap(factory); - - if (mBlockedQuotaManager) { - // Must set mState before dispatching otherwise we will race with the main - // thread. - mState = State_UnblockingQuotaManager; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); - } else { - mState = State_Completed; - } -} - -void -FactoryOp::UnblockQuotaManager() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_UnblockingQuotaManager); - - Nullable persistenceType( - const_cast(mCommonParams.metadata().persistenceType())); - - if (QuotaManager* quotaManager = QuotaManager::Get()) { - quotaManager-> - AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mOrigin), - persistenceType, - mDatabaseId); - } else { - NS_WARNING("QuotaManager went away before we could unblock it!"); - } - - mState = State_Completed; -} - -nsresult -FactoryOp::CheckPermission(ContentParent* aContentParent, - PermissionRequestBase::PermissionValue* aPermission) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_Initial || mState == State_PermissionRetry); - - if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) { - if (aContentParent) { - // The DOM in the other process should have kept us from receiving any - // indexedDB messages so assume that the child is misbehaving. - aContentParent->KillHard(); - } - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } - - if (NS_WARN_IF(mCommonParams.privateBrowsingMode())) { - // XXX This is only temporary. - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } - - const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); - MOZ_ASSERT(principalInfo.type() != PrincipalInfo::TNullPrincipalInfo); - - if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { - MOZ_ASSERT(mState == State_Initial); - - if (aContentParent) { - // Check to make sure that the child process has access to the database it - // is accessing. - NS_NAMED_LITERAL_CSTRING(permissionStringBase, - kPermissionStringChromeBase); - NS_ConvertUTF16toUTF8 databaseName(mCommonParams.metadata().name()); - NS_NAMED_LITERAL_CSTRING(readSuffix, kPermissionStringChromeReadSuffix); - NS_NAMED_LITERAL_CSTRING(writeSuffix, kPermissionStringChromeWriteSuffix); - - const nsAutoCString permissionStringWrite = - permissionStringBase + databaseName + writeSuffix; - const nsAutoCString permissionStringRead = - permissionStringBase + databaseName + readSuffix; - - bool canWrite = - CheckAtLeastOneAppHasPermission(aContentParent, permissionStringWrite); - - bool canRead; - if (canWrite) { - MOZ_ASSERT(CheckAtLeastOneAppHasPermission(aContentParent, - permissionStringRead)); - canRead = true; - } else { - canRead = - CheckAtLeastOneAppHasPermission(aContentParent, permissionStringRead); - } - - // Deleting a database requires write permissions. - if (mDeleting && !canWrite) { - aContentParent->KillHard(); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - // Opening or deleting requires read permissions. - if (!canRead) { - aContentParent->KillHard(); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mChromeWriteAccessAllowed = canWrite; - } else { - mChromeWriteAccessAllowed = true; - } - - if (State_Initial == mState) { - QuotaManager::GetInfoForChrome(&mGroup, &mOrigin, &mStoragePrivilege, - nullptr); - } - - *aPermission = PermissionRequestBase::kPermissionAllowed; - return NS_OK; - } - - MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); - - nsresult rv; - nsCOMPtr principal = - PrincipalInfoToPrincipal(principalInfo, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - PermissionRequestBase::PermissionValue permission; - - if (mCommonParams.metadata().persistenceType() == - PERSISTENCE_TYPE_TEMPORARY) { - // Temporary storage doesn't need to check the permission. - permission = PermissionRequestBase::kPermissionAllowed; - } else { - MOZ_ASSERT(mCommonParams.metadata().persistenceType() == - PERSISTENCE_TYPE_PERSISTENT); - -#ifdef MOZ_CHILD_PERMISSIONS - if (aContentParent) { - if (NS_WARN_IF(!AssertAppPrincipal(aContentParent, principal))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - uint32_t intPermission = - mozilla::CheckPermission(aContentParent, principal, kPermissionString); - - permission = - PermissionRequestBase::PermissionValueForIntPermission(intPermission); - } else -#endif // MOZ_CHILD_PERMISSIONS - { - rv = PermissionRequestBase::GetCurrentPermission(principal, &permission); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - } - - if (permission != PermissionRequestBase::kPermissionDenied && - State_Initial == mState) { - rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &mOrigin, - &mStoragePrivilege, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - *aPermission = permission; - return NS_OK; -} - -nsresult -FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo, - Database* aOpeningDatabase, - uint64_t aOldVersion, - const NullableVersion& aNewVersion) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aDatabaseActorInfo); - MOZ_ASSERT(mState == State_BeginVersionChange); - MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - MOZ_ASSERT(!IsActorDestroyed()); - - const uint32_t expectedCount = mDeleting ? 0 : 1; - const uint32_t liveCount = aDatabaseActorInfo->mLiveDatabases.Length(); - if (liveCount > expectedCount) { - FallibleTArray maybeBlockedDatabases; - for (uint32_t index = 0; index < liveCount; index++) { - Database* database = aDatabaseActorInfo->mLiveDatabases[index]; - if ((!aOpeningDatabase || database != aOpeningDatabase) && - !database->IsClosed() && - NS_WARN_IF(!maybeBlockedDatabases.AppendElement(database))) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - - if (!maybeBlockedDatabases.IsEmpty()) { - mMaybeBlockedDatabases.SwapElements(maybeBlockedDatabases); - } - } - - if (!mMaybeBlockedDatabases.IsEmpty()) { - for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0; - index < count; - /* incremented conditionally */) { - if (mMaybeBlockedDatabases[index]->SendVersionChange(aOldVersion, - aNewVersion)) { - index++; - } else { - // We don't want to wait forever if we were not able to send the - // message. - mMaybeBlockedDatabases.RemoveElementAt(index); - count--; - } - } - } - - return NS_OK; -} - -// static -bool -FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, - const nsACString& aPermissionString) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aContentParent); - MOZ_ASSERT(!aPermissionString.IsEmpty()); - -#ifdef MOZ_CHILD_PERMISSIONS - const nsTArray& browsers = - aContentParent->ManagedPBrowserParent(); - - if (!browsers.IsEmpty()) { - nsCOMPtr appsService = - do_GetService(APPS_SERVICE_CONTRACTID); - if (NS_WARN_IF(!appsService)) { - return false; - } - - nsCOMPtr ioService = do_GetIOService(); - if (NS_WARN_IF(!ioService)) { - return false; - } - - nsCOMPtr secMan = - do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); - if (NS_WARN_IF(!secMan)) { - return false; - } - - nsCOMPtr permMan = - mozilla::services::GetPermissionManager(); - if (NS_WARN_IF(!permMan)) { - return false; - } - - const nsPromiseFlatCString permissionString = - PromiseFlatCString(aPermissionString); - - for (uint32_t index = 0, count = browsers.Length(); - index < count; - index++) { - uint32_t appId = - static_cast(browsers[index])->OwnOrContainingAppId(); - MOZ_ASSERT(kUnknownAppId != appId && kNoAppId != appId); - - nsCOMPtr app; - nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(app)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - nsString origin; - rv = app->GetOrigin(origin); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), origin, nullptr, nullptr, ioService); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - nsCOMPtr principal; - rv = secMan->GetAppCodebasePrincipal(uri, appId, false, - getter_AddRefs(principal)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - uint32_t permission; - rv = permMan->TestExactPermissionFromPrincipal(principal, - permissionString.get(), - &permission); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - if (permission == nsIPermissionManager::ALLOW_ACTION) { - return true; - } - } - } - - return false; -#else - return true; -#endif // MOZ_CHILD_PERMISSIONS -} - -nsresult -FactoryOp::FinishOpen() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_Initial || mState == State_PermissionRetry); - MOZ_ASSERT(!mOrigin.IsEmpty()); - MOZ_ASSERT(!mDatabaseId.IsEmpty()); - MOZ_ASSERT(!mBlockedQuotaManager); - MOZ_ASSERT(!mContentParent); - - PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); - - // XXX This is temporary, but we don't currently support the explicit - // 'persistent' storage type. - if (persistenceType == PERSISTENCE_TYPE_PERSISTENT && - mCommonParams.metadata().persistenceTypeIsExplicit()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - QuotaManager* quotaManager; - - if (QuotaManager::IsShuttingDown() || - !(quotaManager = QuotaManager::GetOrCreate())) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsresult rv = - quotaManager-> - WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mOrigin), - Nullable(persistenceType), - mDatabaseId, - this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mBlockedQuotaManager = true; - - mState = State_OpenPending; - return NS_OK; -} - -void -FactoryOp::NoteDatabaseBlocked(Database* aDatabase) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); - MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty()); - MOZ_ASSERT(mMaybeBlockedDatabases.Contains(aDatabase)); - - // Only send the blocked event if all databases have reported back. If the - // database was closed then it will have been removed from the array. - // Otherwise if it was blocked its |mBlocked| flag will be true. - bool sendBlockedEvent = true; - - for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0; - index < count; - index++) { - MaybeBlockedDatabaseInfo& info = mMaybeBlockedDatabases[index]; - if (info == aDatabase) { - // This database was blocked, mark accordingly. - info.mBlocked = true; - } else if (!info.mBlocked) { - // A database has not yet reported back yet, don't send the event yet. - sendBlockedEvent = false; - } - } - - if (sendBlockedEvent) { - SendBlockedNotification(); - } -} - -NS_IMETHODIMP -FactoryOp::Run() -{ - nsresult rv; - - switch (mState) { - case State_Initial: - rv = Open(); - break; - - case State_OpenPending: - rv = QuotaManagerOpen(); - break; - - case State_PermissionChallenge: - rv = ChallengePermission(); - break; - - case State_PermissionRetry: - rv = RetryCheckPermission(); - break; - - case State_DatabaseWorkOpen: - rv = DoDatabaseWork(); - break; - - case State_BeginVersionChange: - rv = BeginVersionChange(); - break; - - case State_WaitingForTransactionsToComplete: - rv = DispatchToWorkThread(); - break; - - case State_SendingResults: - SendResults(); - return NS_OK; - - case State_UnblockingQuotaManager: - UnblockQuotaManager(); - return NS_OK; - - default: - MOZ_CRASH("Bad state!"); - } - - if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_SendingResults) { - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = rv; - } - - // Must set mState before dispatching otherwise we will race with the owning - // thread. - mState = State_SendingResults; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, - NS_DISPATCH_NORMAL))); - } - - return NS_OK; -} - -void -FactoryOp::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnBackgroundThread(); - - NoteActorDestroyed(); -} - -bool -FactoryOp::RecvPermissionRetry() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!IsActorDestroyed()); - MOZ_ASSERT(mState == State_PermissionChallenge); - - mContentParent = BackgroundParent::GetContentParent(Manager()->Manager()); - - mState = State_PermissionRetry; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); - - return true; -} - -OpenDatabaseOp::OpenDatabaseOp(Factory* aFactory, - already_AddRefed aContentParent, - const OptionalWindowId& aOptionalWindowId, - const CommonFactoryRequestParams& aParams) - : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ false) - , mOptionalWindowId(aOptionalWindowId) - , mMetadata(new FullDatabaseMetadata(aParams.metadata())) - , mRequestedVersion(aParams.metadata().version()) -{ - MOZ_ASSERT_IF(mContentParent, - mOptionalWindowId.type() == OptionalWindowId::Tvoid_t); - - auto& optionalContentParentId = - const_cast(mOptionalContentParentId); - - if (mContentParent) { - // This is a little scary but it looks safe to call this off the main thread - // for now. - optionalContentParentId = mContentParent->ChildID(); - } else { - optionalContentParentId = void_t(); - } -} - -nsresult -OpenDatabaseOp::QuotaManagerOpen() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_OpenPending); - MOZ_ASSERT(!mOfflineStorage); - - QuotaClient* quotaClient = QuotaClient::GetInstance(); - MOZ_ASSERT(quotaClient); - - if (NS_WARN_IF(quotaClient->HasShutDown())) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsRefPtr offlineStorage = - new DatabaseOfflineStorage(quotaClient, - mOptionalWindowId, - mOptionalContentParentId, - mGroup, - mOrigin, - mDatabaseId, - mCommonParams.metadata().persistenceType(), - mOwningThread); - - if (NS_WARN_IF(!quotaManager->RegisterStorage(offlineStorage))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - offlineStorage->NoteRegisteredWithQuotaManager(); - - quotaClient->NoteBackgroundThread(mOwningThread); - - mOfflineStorage.swap(offlineStorage); - - nsresult rv = SendToIOThread(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -OpenDatabaseOp::DoDatabaseWork() -{ - AssertIsOnIOThread(); - MOZ_ASSERT(mState == State_DatabaseWorkOpen); - - PROFILER_LABEL("IndexedDB", - "OpenDatabaseHelper::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - const nsString& databaseName = mCommonParams.metadata().name(); - PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsCOMPtr dbDirectory; - - nsresult rv = - quotaManager->EnsureOriginIsInitialized(persistenceType, - mGroup, - mOrigin, - mStoragePrivilege != Chrome, - getter_AddRefs(dbDirectory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool exists; - rv = dbDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!exists) { - rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } -#ifdef DEBUG - else { - bool isDirectory; - MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory))); - MOZ_ASSERT(isDirectory); - } -#endif - - nsAutoString filename; - GetDatabaseFilename(databaseName, filename); - - nsCOMPtr dbFile; - rv = dbDirectory->Clone(getter_AddRefs(dbFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbFile->GetPath(mDatabaseFilePath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr fmDirectory; - rv = dbDirectory->Clone(getter_AddRefs(fmDirectory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = fmDirectory->Append(filename); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr connection; - rv = CreateDatabaseConnection(dbFile, - fmDirectory, - databaseName, - persistenceType, - mGroup, - mOrigin, - getter_AddRefs(connection)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - AutoSetProgressHandler asph; - rv = asph.Register(this, connection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = LoadDatabaseInformation(connection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(mMetadata->mNextObjectStoreId > mMetadata->mObjectStores.Count()); - MOZ_ASSERT(mMetadata->mNextIndexId > 0); - - // See if we need to do a versionchange transaction - - // Optional version semantics. - if (!mRequestedVersion) { - // If the requested version was not specified and the database was created, - // treat it as if version 1 were requested. - if (mMetadata->mCommonMetadata.version() == 0) { - mRequestedVersion = 1; - } else { - // Otherwise, treat it as if the current version were requested. - mRequestedVersion = mMetadata->mCommonMetadata.version(); - } - } - - if (NS_WARN_IF(mMetadata->mCommonMetadata.version() > mRequestedVersion)) { - return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR; - } - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - MOZ_ASSERT(mgr); - - nsRefPtr fileManager = - mgr->GetFileManager(persistenceType, mOrigin, databaseName); - if (!fileManager) { - fileManager = new FileManager(persistenceType, - mGroup, - mOrigin, - mStoragePrivilege, - databaseName); - - rv = fileManager->Init(fmDirectory, connection); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mgr->AddFileManager(fileManager); - } - - mFileManager = fileManager.forget(); - - // Must set mState before dispatching otherwise we will race with the owning - // thread. - mState = (mMetadata->mCommonMetadata.version() == mRequestedVersion) ? - State_SendingResults : - State_BeginVersionChange; - - rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aConnection); - MOZ_ASSERT(mMetadata); - - // Load version information. - nsCOMPtr stmt; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name, version " - "FROM database" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!hasResult)) { - return NS_ERROR_FILE_CORRUPTED; - } - - nsString databaseName; - rv = stmt->GetString(0, databaseName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(mCommonParams.metadata().name() != databaseName)) { - return NS_ERROR_FILE_CORRUPTED; - } - - int64_t version; - rv = stmt->GetInt64(1, &version); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mMetadata->mCommonMetadata.version() = uint64_t(version); - - ObjectStoreTable& objectStores = mMetadata->mObjectStores; - - // Load object store names and ids. - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, auto_increment, name, key_path " - "FROM object_store" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - Maybe> usedIds; - Maybe> usedNames; - - int64_t lastObjectStoreId = 0; - - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - int64_t objectStoreId; - rv = stmt->GetInt64(0, &objectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!usedIds) { - usedIds.emplace(); - } - - if (NS_WARN_IF(objectStoreId <= 0) || - NS_WARN_IF(usedIds.ref().Contains(objectStoreId))) { - return NS_ERROR_FILE_CORRUPTED; - } - - if (NS_WARN_IF(!usedIds.ref().PutEntry(objectStoreId, fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsString name; - rv = stmt->GetString(2, name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!usedNames) { - usedNames.emplace(); - } - - if (NS_WARN_IF(usedNames.ref().Contains(name))) { - return NS_ERROR_FILE_CORRUPTED; - } - - if (NS_WARN_IF(!usedNames.ref().PutEntry(name, fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsRefPtr metadata = new FullObjectStoreMetadata(); - metadata->mCommonMetadata.id() = objectStoreId; - metadata->mCommonMetadata.name() = name; - - int32_t columnType; - rv = stmt->GetTypeOfIndex(3, &columnType); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) { - metadata->mCommonMetadata.keyPath() = KeyPath(0); - } else { - MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_TEXT); - - nsString keyPathSerialization; - rv = stmt->GetString(3, keyPathSerialization); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - metadata->mCommonMetadata.keyPath() = - KeyPath::DeserializeFromString(keyPathSerialization); - if (NS_WARN_IF(!metadata->mCommonMetadata.keyPath().IsValid())) { - return NS_ERROR_FILE_CORRUPTED; - } - } - - int64_t nextAutoIncrementId; - rv = stmt->GetInt64(1, &nextAutoIncrementId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - metadata->mCommonMetadata.autoIncrement() = !!nextAutoIncrementId; - metadata->mNextAutoIncrementId = nextAutoIncrementId; - metadata->mComittedAutoIncrementId = nextAutoIncrementId; - - if (NS_WARN_IF(!objectStores.Put(objectStoreId, metadata, fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - lastObjectStoreId = std::max(lastObjectStoreId, objectStoreId); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - usedIds.reset(); - usedNames.reset(); - - // Load index information - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, object_store_id, name, key_path, unique_index, multientry " - "FROM object_store_index" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int64_t lastIndexId = 0; - - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - int64_t objectStoreId; - rv = stmt->GetInt64(1, &objectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsRefPtr objectStoreMetadata; - if (NS_WARN_IF(!objectStores.Get(objectStoreId, - getter_AddRefs(objectStoreMetadata)))) { - return NS_ERROR_FILE_CORRUPTED; - } - - MOZ_ASSERT(objectStoreMetadata->mCommonMetadata.id() == objectStoreId); - - int64_t indexId; - rv = stmt->GetInt64(0, &indexId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!usedIds) { - usedIds.emplace(); - } - - if (NS_WARN_IF(indexId <= 0) || - NS_WARN_IF(usedIds.ref().Contains(indexId))) { - return NS_ERROR_FILE_CORRUPTED; - } - - if (NS_WARN_IF(!usedIds.ref().PutEntry(indexId, fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsString name; - rv = stmt->GetString(2, name); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoString hashName; - hashName.AppendInt(indexId); - hashName.Append(':'); - hashName.Append(name); - - if (!usedNames) { - usedNames.emplace(); - } - - if (NS_WARN_IF(usedNames.ref().Contains(hashName))) { - return NS_ERROR_FILE_CORRUPTED; - } - - if (NS_WARN_IF(!usedNames.ref().PutEntry(hashName, fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - nsRefPtr indexMetadata = new FullIndexMetadata(); - indexMetadata->mCommonMetadata.id() = indexId; - indexMetadata->mCommonMetadata.name() = name; - -#ifdef DEBUG - { - int32_t columnType; - rv = stmt->GetTypeOfIndex(3, &columnType); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - MOZ_ASSERT(columnType != mozIStorageStatement::VALUE_TYPE_NULL); - } -#endif - - nsString keyPathSerialization; - rv = stmt->GetString(3, keyPathSerialization); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - indexMetadata->mCommonMetadata.keyPath() = - KeyPath::DeserializeFromString(keyPathSerialization); - if (NS_WARN_IF(!indexMetadata->mCommonMetadata.keyPath().IsValid())) { - return NS_ERROR_FILE_CORRUPTED; - } - - int32_t scratch; - rv = stmt->GetInt32(4, &scratch); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - indexMetadata->mCommonMetadata.unique() = !!scratch; - - rv = stmt->GetInt32(5, &scratch); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - indexMetadata->mCommonMetadata.multiEntry() = !!scratch; - - if (NS_WARN_IF(!objectStoreMetadata->mIndexes.Put(indexId, indexMetadata, - fallible))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - lastIndexId = std::max(lastIndexId, indexId); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(lastObjectStoreId == INT64_MAX) || - NS_WARN_IF(lastIndexId == INT64_MAX)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mMetadata->mNextObjectStoreId = lastObjectStoreId + 1; - mMetadata->mNextIndexId = lastIndexId + 1; - - return NS_OK; -} - -nsresult -OpenDatabaseOp::BeginVersionChange() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_BeginVersionChange); - MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - MOZ_ASSERT(mMetadata->mCommonMetadata.version() <= mRequestedVersion); - MOZ_ASSERT(!mDatabase); - MOZ_ASSERT(!mVersionChangeTransaction); - - if (IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - EnsureDatabaseActor(); - - if (mDatabase->IsInvalidated()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - MOZ_ASSERT(!mDatabase->IsClosed()); - - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info)); - - MOZ_ASSERT(info->mLiveDatabases.Contains(mDatabase)); - MOZ_ASSERT(!info->mWaitingFactoryOp); - MOZ_ASSERT(info->mMetadata == mMetadata); - - nsRefPtr transaction = - new VersionChangeTransaction(this); - - if (NS_WARN_IF(!transaction->CopyDatabaseMetadata())) { - return NS_ERROR_OUT_OF_MEMORY; - } - - MOZ_ASSERT(info->mMetadata != mMetadata); - mMetadata = info->mMetadata; - - NullableVersion newVersion = mRequestedVersion; - - nsresult rv = - SendVersionChangeMessages(info, - mDatabase, - mMetadata->mCommonMetadata.version(), - newVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mVersionChangeTransaction.swap(transaction); - - if (mMaybeBlockedDatabases.IsEmpty()) { - // We don't need to wait on any databases, just jump to the transaction - // pool. - WaitForTransactions(); - return NS_OK; - } - - info->mWaitingFactoryOp = this; - - mState = State_WaitingForOtherDatabasesToClose; - return NS_OK; -} - -void -OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); - MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty()); - - bool actorDestroyed = IsActorDestroyed() || mDatabase->IsActorDestroyed(); - - nsresult rv; - if (actorDestroyed) { - IDB_REPORT_INTERNAL_ERR(); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } else { - rv = NS_OK; - } - - if (mMaybeBlockedDatabases.RemoveElement(aDatabase) && - mMaybeBlockedDatabases.IsEmpty()) { - if (actorDestroyed) { - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info)); - MOZ_ASSERT(info->mWaitingFactoryOp == this); - info->mWaitingFactoryOp = nullptr; - } else { - WaitForTransactions(); - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = rv; - } - - mState = State_SendingResults; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run())); - } -} - -void -OpenDatabaseOp::SendBlockedNotification() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); - - if (!IsActorDestroyed()) { - unused << SendBlocked(mMetadata->mCommonMetadata.version()); - } -} - -nsresult -OpenDatabaseOp::DispatchToWorkThread() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete); - MOZ_ASSERT(mVersionChangeTransaction); - MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - - if (IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mState = State_DatabaseWorkVersionChange; - - TransactionThreadPool* threadPool = mFactory->GetTransactionThreadPool(); - MOZ_ASSERT(threadPool); - - // Intentionally empty. - nsTArray objectStoreNames; - - nsRefPtr versionChangeOp = new VersionChangeOp(this); - - threadPool->Dispatch(mVersionChangeTransaction->TransactionId(), - mVersionChangeTransaction->DatabaseId(), - objectStoreNames, - mVersionChangeTransaction->GetMode(), - versionChangeOp, - /* aFinish */ false, - /* aFinishCallback */ nullptr); - - mVersionChangeTransaction->SetActive(); - - mVersionChangeTransaction->NoteActiveRequest(); - - if (NS_WARN_IF(!mDatabase->RegisterTransaction(mVersionChangeTransaction))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - return NS_OK; -} - -nsresult -OpenDatabaseOp::SendUpgradeNeeded() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_DatabaseWorkVersionChange); - MOZ_ASSERT(mVersionChangeTransaction); - MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); - MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase); - - if (IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsRefPtr transaction; - mVersionChangeTransaction.swap(transaction); - - nsresult rv = EnsureDatabaseActorIsAlive(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Transfer ownership to IPDL. - transaction->SetActorAlive(); - - if (!mDatabase->SendPBackgroundIDBVersionChangeTransactionConstructor( - transaction, - mMetadata->mCommonMetadata.version(), - mRequestedVersion, - mMetadata->mNextObjectStoreId, - mMetadata->mNextIndexId)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return NS_OK; -} - -void -OpenDatabaseOp::SendResults() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_SendingResults); - MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), mMaybeBlockedDatabases.IsEmpty()); - MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), !mVersionChangeTransaction); - - mMaybeBlockedDatabases.Clear(); - - // Only needed if we're being called from within NoteDatabaseDone() since this - // OpenDatabaseOp is only held alive by the gLiveDatabaseHashtable. - nsRefPtr kungFuDeathGrip; - - DatabaseActorInfo* info; - if (gLiveDatabaseHashtable->Get(mDatabaseId, &info) && - info->mWaitingFactoryOp) { - MOZ_ASSERT(info->mWaitingFactoryOp == this); - kungFuDeathGrip = - static_cast(info->mWaitingFactoryOp.get()); - info->mWaitingFactoryOp = nullptr; - } - - if (mVersionChangeTransaction) { - MOZ_ASSERT(NS_FAILED(mResultCode)); - - mVersionChangeTransaction->Abort(mResultCode, /* aForce */ true); - mVersionChangeTransaction = nullptr; - } - - if (!IsActorDestroyed() && - (!mDatabase || !mDatabase->IsInvalidated())) { - FactoryRequestResponse response; - - if (NS_SUCCEEDED(mResultCode)) { - // If we just successfully completed a versionchange operation then we - // need to update the version in our metadata. - mMetadata->mCommonMetadata.version() = mRequestedVersion; - - nsresult rv = EnsureDatabaseActorIsAlive(); - if (NS_SUCCEEDED(rv)) { - // We successfully opened a database so use its actor as the success - // result for this request. - OpenDatabaseRequestResponse openResponse; - openResponse.databaseParent() = mDatabase; - response = openResponse; - } else { - response = ClampResultCode(rv); -#ifdef DEBUG - mResultCode = response.get_nsresult(); -#endif - } - } else { -#ifdef DEBUG - // If something failed then our metadata pointer is now bad. No one should - // ever touch it again though so just null it out in DEBUG builds to make - // sure we find such cases. - mMetadata = nullptr; -#endif - response = ClampResultCode(mResultCode); - } - - NS_WARN_IF(!PBackgroundIDBFactoryRequestParent::Send__delete__(this, - response)); - } - - if (NS_FAILED(mResultCode) && mOfflineStorage) { - mOfflineStorage->CloseOnOwningThread(); - DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget()); - } - - FinishSendResults(); -} - -void -OpenDatabaseOp::EnsureDatabaseActor() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_BeginVersionChange || - mState == State_DatabaseWorkVersionChange || - mState == State_SendingResults); - MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); - MOZ_ASSERT(!mDatabaseFilePath.IsEmpty()); - MOZ_ASSERT(!IsActorDestroyed()); - - if (mDatabase) { - return; - } - - MOZ_ASSERT(mMetadata->mDatabaseId.IsEmpty()); - mMetadata->mDatabaseId = mDatabaseId; - - MOZ_ASSERT(mMetadata->mFilePath.IsEmpty()); - mMetadata->mFilePath = mDatabaseFilePath; - - DatabaseActorInfo* info; - if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) { - AssertMetadataConsistency(info->mMetadata); - mMetadata = info->mMetadata; - } - - auto factory = static_cast(Manager()); - - mDatabase = new Database(factory, - mCommonParams.principalInfo(), - mGroup, - mOrigin, - mMetadata, - mFileManager, - mOfflineStorage.forget(), - mChromeWriteAccessAllowed); - - if (info) { - info->mLiveDatabases.AppendElement(mDatabase); - } else { - info = new DatabaseActorInfo(mMetadata, mDatabase); - gLiveDatabaseHashtable->Put(mDatabaseId, info); - } - - QuotaClient::GetInstance()->NoteNewTransactionThreadPool(); -} - -nsresult -OpenDatabaseOp::EnsureDatabaseActorIsAlive() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_DatabaseWorkVersionChange || - mState == State_SendingResults); - MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); - MOZ_ASSERT(!IsActorDestroyed()); - - EnsureDatabaseActor(); - - if (mDatabase->IsActorAlive()) { - return NS_OK; - } - - auto factory = static_cast(Manager()); - - DatabaseSpec spec; - MetadataToSpec(spec); - - // Transfer ownership to IPDL. - mDatabase->SetActorAlive(); - - if (!factory->SendPBackgroundIDBDatabaseConstructor(mDatabase, spec, this)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return NS_OK; -} - -void -OpenDatabaseOp::MetadataToSpec(DatabaseSpec& aSpec) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mMetadata); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - DatabaseSpec& mSpec; - ObjectStoreSpec* mCurrentObjectStoreSpec; - - public: - static void - CopyToSpec(const FullDatabaseMetadata* aMetadata, DatabaseSpec& aSpec) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aMetadata); - - aSpec.metadata() = aMetadata->mCommonMetadata; - - Helper helper(aSpec); - aMetadata->mObjectStores.EnumerateRead(Enumerate, &helper); - } - - private: - explicit Helper(DatabaseSpec& aSpec) - : mSpec(aSpec) - , mCurrentObjectStoreSpec(nullptr) - { } - - static PLDHashOperator - Enumerate(const uint64_t& aKey, - FullObjectStoreMetadata* aValue, - void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* helper = static_cast(aClosure); - - MOZ_ASSERT(!helper->mCurrentObjectStoreSpec); - - // XXX This should really be fallible... - ObjectStoreSpec* objectStoreSpec = - helper->mSpec.objectStores().AppendElement(); - objectStoreSpec->metadata() = aValue->mCommonMetadata; - - AutoRestore ar(helper->mCurrentObjectStoreSpec); - helper->mCurrentObjectStoreSpec = objectStoreSpec; - - aValue->mIndexes.EnumerateRead(Enumerate, helper); - - return PL_DHASH_NEXT; - } - - static PLDHashOperator - Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - auto* helper = static_cast(aClosure); - - MOZ_ASSERT(helper->mCurrentObjectStoreSpec); - - // XXX This should really be fallible... - IndexMetadata* metadata = - helper->mCurrentObjectStoreSpec->indexes().AppendElement(); - *metadata = aValue->mCommonMetadata; - - return PL_DHASH_NEXT; - } - }; - - Helper::CopyToSpec(mMetadata, aSpec); -} - - -#ifdef DEBUG - -void -OpenDatabaseOp::AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata) -{ - AssertIsOnBackgroundThread(); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - const ObjectStoreTable& mOtherObjectStores; - IndexTable* mCurrentOtherIndexTable; - - public: - static void - AssertConsistent(const ObjectStoreTable& aThisObjectStores, - const ObjectStoreTable& aOtherObjectStores) - { - Helper helper(aOtherObjectStores); - aThisObjectStores.EnumerateRead(Enumerate, &helper); - } - - private: - explicit Helper(const ObjectStoreTable& aOtherObjectStores) - : mOtherObjectStores(aOtherObjectStores) - , mCurrentOtherIndexTable(nullptr) - { } - - static PLDHashOperator - Enumerate(const uint64_t& /* aKey */, - FullObjectStoreMetadata* aThisObjectStore, - void* aClosure) - { - MOZ_ASSERT(aThisObjectStore); - MOZ_ASSERT(!aThisObjectStore->mDeleted); - MOZ_ASSERT(aClosure); - - auto* helper = static_cast(aClosure); - - MOZ_ASSERT(!helper->mCurrentOtherIndexTable); - - auto* otherObjectStore = - MetadataNameOrIdMatcher::Match( - helper->mOtherObjectStores, aThisObjectStore->mCommonMetadata.id()); - MOZ_ASSERT(otherObjectStore); - - MOZ_ASSERT(aThisObjectStore != otherObjectStore); - - MOZ_ASSERT(aThisObjectStore->mCommonMetadata.id() == - otherObjectStore->mCommonMetadata.id()); - MOZ_ASSERT(aThisObjectStore->mCommonMetadata.name() == - otherObjectStore->mCommonMetadata.name()); - MOZ_ASSERT(aThisObjectStore->mCommonMetadata.autoIncrement() == - otherObjectStore->mCommonMetadata.autoIncrement()); - MOZ_ASSERT(aThisObjectStore->mCommonMetadata.keyPath() == - otherObjectStore->mCommonMetadata.keyPath()); - // mNextAutoIncrementId and mComittedAutoIncrementId may be modified - // concurrently with this OpenOp, so it is not possible to assert equality - // here. - MOZ_ASSERT(aThisObjectStore->mNextAutoIncrementId <= - otherObjectStore->mNextAutoIncrementId); - MOZ_ASSERT(aThisObjectStore->mComittedAutoIncrementId <= - otherObjectStore->mComittedAutoIncrementId); - MOZ_ASSERT(!otherObjectStore->mDeleted); - - MOZ_ASSERT(aThisObjectStore->mIndexes.Count() == - otherObjectStore->mIndexes.Count()); - - AutoRestore ar(helper->mCurrentOtherIndexTable); - helper->mCurrentOtherIndexTable = &otherObjectStore->mIndexes; - - aThisObjectStore->mIndexes.EnumerateRead(Enumerate, helper); - - return PL_DHASH_NEXT; - } - - static PLDHashOperator - Enumerate(const uint64_t& /* aKey */, - FullIndexMetadata* aThisIndex, - void* aClosure) - { - MOZ_ASSERT(aThisIndex); - MOZ_ASSERT(!aThisIndex->mDeleted); - MOZ_ASSERT(aClosure); - - auto* helper = static_cast(aClosure); - - MOZ_ASSERT(helper->mCurrentOtherIndexTable); - - auto* otherIndex = - MetadataNameOrIdMatcher::Match( - *helper->mCurrentOtherIndexTable, aThisIndex->mCommonMetadata.id()); - MOZ_ASSERT(otherIndex); - - MOZ_ASSERT(aThisIndex != otherIndex); - - MOZ_ASSERT(aThisIndex->mCommonMetadata.id() == - otherIndex->mCommonMetadata.id()); - MOZ_ASSERT(aThisIndex->mCommonMetadata.name() == - otherIndex->mCommonMetadata.name()); - MOZ_ASSERT(aThisIndex->mCommonMetadata.keyPath() == - otherIndex->mCommonMetadata.keyPath()); - MOZ_ASSERT(aThisIndex->mCommonMetadata.unique() == - otherIndex->mCommonMetadata.unique()); - MOZ_ASSERT(aThisIndex->mCommonMetadata.multiEntry() == - otherIndex->mCommonMetadata.multiEntry()); - MOZ_ASSERT(!otherIndex->mDeleted); - - return PL_DHASH_NEXT; - } - }; - - const FullDatabaseMetadata* thisDB = mMetadata; - const FullDatabaseMetadata* otherDB = aMetadata; - - MOZ_ASSERT(thisDB); - MOZ_ASSERT(otherDB); - MOZ_ASSERT(thisDB != otherDB); - - MOZ_ASSERT(thisDB->mCommonMetadata.name() == otherDB->mCommonMetadata.name()); - MOZ_ASSERT(thisDB->mCommonMetadata.version() == - otherDB->mCommonMetadata.version()); - MOZ_ASSERT(thisDB->mCommonMetadata.persistenceType() == - otherDB->mCommonMetadata.persistenceType()); - MOZ_ASSERT(thisDB->mDatabaseId == otherDB->mDatabaseId); - MOZ_ASSERT(thisDB->mFilePath == otherDB->mFilePath); - - // The newer database metadata (db2) reflects the latest objectStore and index - // ids that have committed to disk. The in-memory metadata (db1) keeps track - // of objectStores and indexes that were created and then removed as well, so - // the next ids for db1 may be higher than for db2. - MOZ_ASSERT(thisDB->mNextObjectStoreId >= otherDB->mNextObjectStoreId); - MOZ_ASSERT(thisDB->mNextIndexId >= otherDB->mNextIndexId); - - MOZ_ASSERT(thisDB->mObjectStores.Count() == otherDB->mObjectStores.Count()); - - Helper::AssertConsistent(thisDB->mObjectStores, otherDB->mObjectStores); -} - -#endif // DEBUG - -nsresult -OpenDatabaseOp:: -VersionChangeOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); - - PROFILER_LABEL("IndexedDB", - "VersionChangeOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - mozIStorageConnection* connection = aTransaction->Connection(); - MOZ_ASSERT(connection); - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr stmt; - rv = connection->CreateStatement( - NS_LITERAL_CSTRING("UPDATE database " - "SET version = :version"), - getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"), - int64_t(mRequestedVersion)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -OpenDatabaseOp:: -VersionChangeOp::SendSuccessResult() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mOpenDatabaseOp); - MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); - - nsresult rv = mOpenDatabaseOp->SendUpgradeNeeded(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -bool -OpenDatabaseOp:: -VersionChangeOp::SendFailureResult(nsresult aResultCode) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mOpenDatabaseOp); - MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); - - mOpenDatabaseOp->SetFailureCode(aResultCode); - mOpenDatabaseOp->mState = State_SendingResults; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOpenDatabaseOp->Run())); - - return false; -} - -void -OpenDatabaseOp:: -VersionChangeOp::Cleanup() -{ - AssertIsOnOwningThread(); - - mOpenDatabaseOp = nullptr; - -#ifdef DEBUG - // A bit hacky but the VersionChangeOp is not generated in response to a - // child request like most other database operations. Do this to make our - // assertions happy. - NoteActorDestroyed(); -#endif - - TransactionDatabaseOperationBase::Cleanup(); -} - -void -DeleteDatabaseOp::LoadPreviousVersion(nsIFile* aDatabaseFile) -{ - AssertIsOnIOThread(); - MOZ_ASSERT(aDatabaseFile); - MOZ_ASSERT(mState == State_DatabaseWorkOpen); - MOZ_ASSERT(!mPreviousVersion); - - PROFILER_LABEL("IndexedDB", - "DeleteDatabaseOp::LoadPreviousVersion", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - - nsCOMPtr ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - nsCOMPtr connection; - rv = ss->OpenDatabase(aDatabaseFile, getter_AddRefs(connection)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - -#ifdef DEBUG - { - nsCOMPtr stmt; - rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name " - "FROM database" - ), getter_AddRefs(stmt)); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "CreateStatement failed!"); - - if (NS_SUCCEEDED(rv)) { - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "ExecuteStep failed!"); - - if (NS_SUCCEEDED(rv)) { - nsString databaseName; - rv = stmt->GetString(0, databaseName); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "GetString failed!"); - - if (NS_SUCCEEDED(rv)) { - NS_WARN_IF_FALSE(mCommonParams.metadata().name() == databaseName, - "Database names don't match!"); - } - } - } - } -#endif - - nsCOMPtr stmt; - rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT version " - "FROM database" - ), getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - if (NS_WARN_IF(!hasResult)) { - return; - } - - int64_t version; - rv = stmt->GetInt64(0, &version); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - mPreviousVersion = uint64_t(version); -} - -nsresult -DeleteDatabaseOp::QuotaManagerOpen() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_OpenPending); - - // Swap this to the stack now to ensure that we release it on this thread. - nsRefPtr contentParent; - mContentParent.swap(contentParent); - - nsresult rv = SendToIOThread(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -DeleteDatabaseOp::DoDatabaseWork() -{ - AssertIsOnIOThread(); - MOZ_ASSERT(mState == State_DatabaseWorkOpen); - - PROFILER_LABEL("IndexedDB", - "DeleteDatabaseOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (!OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - const nsString& databaseName = mCommonParams.metadata().name(); - PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsCOMPtr directory; - nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType, - mOrigin, - getter_AddRefs(directory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = directory->GetPath(mDatabaseDirectoryPath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoString filename; - GetDatabaseFilename(databaseName, filename); - - mDatabaseFilenameBase = filename; - - nsCOMPtr dbFile; - rv = directory->Clone(getter_AddRefs(dbFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool exists; - rv = dbFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - // Parts of this function may fail but that shouldn't prevent us from - // deleting the file eventually. - LoadPreviousVersion(dbFile); - - mState = State_BeginVersionChange; - } else { - mState = State_SendingResults; - } - - rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -DeleteDatabaseOp::BeginVersionChange() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_BeginVersionChange); - MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - - if (IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - DatabaseActorInfo* info; - if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) { - MOZ_ASSERT(!info->mWaitingFactoryOp); - - NullableVersion newVersion = null_t(); - - nsresult rv = - SendVersionChangeMessages(info, nullptr, mPreviousVersion, newVersion); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!mMaybeBlockedDatabases.IsEmpty()) { - info->mWaitingFactoryOp = this; - - mState = State_WaitingForOtherDatabasesToClose; - return NS_OK; - } - } - - // No other databases need to be notified, just make sure that all - // transactions are complete. - WaitForTransactions(); - return NS_OK; -} - -nsresult -DeleteDatabaseOp::DispatchToWorkThread() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete); - MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - - if (IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mState = State_DatabaseWorkVersionChange; - - nsRefPtr versionChangeOp = new VersionChangeOp(this); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(versionChangeOp))); - - return NS_OK; -} - -void -DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); - MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty()); - - bool actorDestroyed = IsActorDestroyed(); - - nsresult rv; - if (actorDestroyed) { - IDB_REPORT_INTERNAL_ERR(); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } else { - rv = NS_OK; - } - - if (mMaybeBlockedDatabases.RemoveElement(aDatabase) && - mMaybeBlockedDatabases.IsEmpty()) { - if (actorDestroyed) { - DatabaseActorInfo* info; - MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info)); - MOZ_ASSERT(info->mWaitingFactoryOp == this); - info->mWaitingFactoryOp = nullptr; - } else { - WaitForTransactions(); - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = rv; - } - - mState = State_SendingResults; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run())); - } -} - -void -DeleteDatabaseOp::SendBlockedNotification() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); - - if (!IsActorDestroyed()) { - unused << SendBlocked(0); - } -} - -void -DeleteDatabaseOp::SendResults() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State_SendingResults); - - if (!IsActorDestroyed()) { - FactoryRequestResponse response; - - if (NS_SUCCEEDED(mResultCode)) { - response = DeleteDatabaseRequestResponse(mPreviousVersion); - } else { - response = ClampResultCode(mResultCode); - } - - NS_WARN_IF(!PBackgroundIDBFactoryRequestParent::Send__delete__(this, - response)); - } - - FinishSendResults(); -} - -nsresult -DeleteDatabaseOp:: -VersionChangeOp::RunOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - return NS_OK; -} - -nsresult -DeleteDatabaseOp:: -VersionChangeOp::RunOnIOThread() -{ - AssertIsOnIOThread(); - MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); - - PROFILER_LABEL("IndexedDB", - "DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", - js::ProfileEntry::Category::STORAGE); - - if (!OperationMayProceed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr directory = - GetFileForPath(mDeleteDatabaseOp->mDatabaseDirectoryPath); - if (NS_WARN_IF(!directory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr dbFile; - nsresult rv = directory->Clone(getter_AddRefs(dbFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbFile->Append(mDeleteDatabaseOp->mDatabaseFilenameBase + - NS_LITERAL_STRING(".sqlite")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool exists; - rv = dbFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - const nsString& databaseName = - mDeleteDatabaseOp->mCommonParams.metadata().name(); - PersistenceType persistenceType = - mDeleteDatabaseOp->mCommonParams.metadata().persistenceType(); - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - if (exists) { - int64_t fileSize; - - if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { - rv = dbFile->GetFileSize(&fileSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = dbFile->Remove(false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { - quotaManager->DecreaseUsageForOrigin(persistenceType, - mDeleteDatabaseOp->mGroup, - mDeleteDatabaseOp->mOrigin, - fileSize); - } - } - - nsCOMPtr dbJournalFile; - rv = directory->Clone(getter_AddRefs(dbJournalFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbJournalFile->Append(mDeleteDatabaseOp->mDatabaseFilenameBase + - NS_LITERAL_STRING(".sqlite-journal")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = dbJournalFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - rv = dbJournalFile->Remove(false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - nsCOMPtr fmDirectory; - rv = directory->Clone(getter_AddRefs(fmDirectory)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = fmDirectory->Append(mDeleteDatabaseOp->mDatabaseFilenameBase); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = fmDirectory->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (exists) { - bool isDirectory; - rv = fmDirectory->IsDirectory(&isDirectory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!isDirectory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - uint64_t usage = 0; - - if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { - rv = FileManager::GetUsage(fmDirectory, &usage); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = fmDirectory->Remove(true); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { - quotaManager->DecreaseUsageForOrigin(persistenceType, - mDeleteDatabaseOp->mGroup, - mDeleteDatabaseOp->mOrigin, - usage); - } - } - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - MOZ_ASSERT(mgr); - - mgr->InvalidateFileManager(persistenceType, - mDeleteDatabaseOp->mOrigin, - databaseName); - - rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -void -DeleteDatabaseOp:: -VersionChangeOp::RunOnOwningThread() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); - - nsRefPtr deleteOp; - mDeleteDatabaseOp.swap(deleteOp); - - if (deleteOp->IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - deleteOp->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } else { - DatabaseActorInfo* info; - if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId, &info) && - info->mWaitingFactoryOp) { - MOZ_ASSERT(info->mWaitingFactoryOp == deleteOp); - info->mWaitingFactoryOp = nullptr; - } - - if (NS_FAILED(mResultCode)) { - if (NS_SUCCEEDED(deleteOp->ResultCode())) { - deleteOp->SetFailureCode(mResultCode); - } - } else { - // Inform all the other databases that they are now invalidated. That - // should remove the previous metadata from our table. - if (info) { - MOZ_ASSERT(!info->mLiveDatabases.IsEmpty()); - - FallibleTArray liveDatabases; - if (NS_WARN_IF(!liveDatabases.AppendElements(info->mLiveDatabases))) { - deleteOp->SetFailureCode(NS_ERROR_OUT_OF_MEMORY); - } else { -#ifdef DEBUG - // The code below should result in the deletion of |info|. Set to null - // here to make sure we find invalid uses later. - info = nullptr; -#endif - for (uint32_t count = liveDatabases.Length(), index = 0; - index < count; - index++) { - nsRefPtr database = liveDatabases[index]; - database->Invalidate(); - } - - MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId)); - } - } - } - } - - deleteOp->mState = State_SendingResults; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(deleteOp->Run())); - -#ifdef DEBUG - // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a - // normal database operation that is tied to an actor. Do this to make our - // assertions happy. - NoteActorDestroyed(); -#endif -} - -nsresult -DeleteDatabaseOp:: -VersionChangeOp::Run() -{ - nsresult rv; - - if (NS_IsMainThread()) { - rv = RunOnMainThread(); - } else if (!IsOnBackgroundThread()) { - rv = RunOnIOThread(); - } else { - RunOnOwningThread(); - rv = NS_OK; - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = rv; - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, - NS_DISPATCH_NORMAL))); - } - - return NS_OK; -} - -TransactionDatabaseOperationBase::TransactionDatabaseOperationBase( - TransactionBase* aTransaction) - : mTransaction(aTransaction) - , mTransactionIsAborted(aTransaction->IsAborted()) -{ - MOZ_ASSERT(aTransaction); -} - -TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase() -{ - MOZ_ASSERT(!mTransaction, - "TransactionDatabaseOperationBase::Cleanup() was not called by a " - "subclass!"); -} - -#ifdef DEBUG - -void -TransactionDatabaseOperationBase::AssertIsOnTransactionThread() const -{ - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnTransactionThread(); -} - -#endif // DEBUG - -void -TransactionDatabaseOperationBase::DispatchToTransactionThreadPool() -{ - AssertIsOnOwningThread(); - - TransactionThreadPool* threadPool = mTransaction->GetTransactionThreadPool(); - MOZ_ASSERT(threadPool); - - threadPool->Dispatch(mTransaction->TransactionId(), - mTransaction->DatabaseId(), - this, - /* aFinish */ false, - /* aFinishCallback */ nullptr); - - mTransaction->NoteActiveRequest(); -} - -void -TransactionDatabaseOperationBase::RunOnTransactionThread() -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); - - // There are several cases where we don't actually have to to any work here. - - if (mTransactionIsAborted) { - // This transaction is already set to be aborted. - mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; - } else if (mTransaction->IsInvalidatedOnAnyThread()) { - // This transaction is being invalidated. - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } else if (!OperationMayProceed()) { - // The operation was canceled in some way, likely because the child process - // has crashed. - IDB_REPORT_INTERNAL_ERR(); - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } else { - // Here we're actually going to perform the database operation. - nsresult rv = mTransaction->EnsureConnection(); - if (NS_WARN_IF(NS_FAILED(rv))) { - mResultCode = rv; - } else { - mTransaction->AssertIsOnTransactionThread(); - - AutoSetProgressHandler autoProgress; - rv = autoProgress.Register(this, mTransaction->Connection()); - if (NS_WARN_IF(NS_FAILED(rv))) { - mResultCode = rv; - } else { - rv = DoDatabaseWork(mTransaction); - if (NS_FAILED(rv)) { - mResultCode = rv; - } - } - } - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, - NS_DISPATCH_NORMAL))); -} - -void -TransactionDatabaseOperationBase::RunOnOwningThread() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mTransaction); - - if (NS_WARN_IF(IsActorDestroyed())) { - // Don't send any notifications if the actor was destroyed already. - if (NS_SUCCEEDED(mResultCode)) { - IDB_REPORT_INTERNAL_ERR(); - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } else { - if (mTransaction->IsInvalidated()) { - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } else if (mTransaction->IsAborted()) { - // Aborted transactions always see their requests fail with ABORT_ERR, - // even if the request succeeded or failed with another error. - mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; - } else if (NS_SUCCEEDED(mResultCode)) { - // This may release the IPDL reference. - mResultCode = SendSuccessResult(); - } - - if (NS_FAILED(mResultCode)) { - // This should definitely release the IPDL reference. - if (!SendFailureResult(mResultCode)) { - // Abort the transaction. - mTransaction->Abort(mResultCode, /* aForce */ false); - } - } - } - - mTransaction->NoteFinishedRequest(); - - Cleanup(); -} - -bool -TransactionDatabaseOperationBase::Init(TransactionBase* aTransaction) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aTransaction); - - return true; -} - -void -TransactionDatabaseOperationBase::Cleanup() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mTransaction); - - mTransaction = nullptr; -} - -NS_IMETHODIMP -TransactionDatabaseOperationBase::Run() -{ - MOZ_ASSERT(mTransaction); - - if (IsOnBackgroundThread()) { - RunOnOwningThread(); - } else { - RunOnTransactionThread(); - } - - return NS_OK; -} - -nsresult -TransactionBase:: -CommitOp::WriteAutoIncrementCounts() -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(mTransaction); - - const nsTArray>& metadataArray = - mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray; - - nsCOMPtr stmt; - nsresult rv; - - if (!metadataArray.IsEmpty()) { - NS_NAMED_LITERAL_CSTRING(osid, "osid"); - NS_NAMED_LITERAL_CSTRING(ai, "ai"); - - for (uint32_t count = metadataArray.Length(), index = 0; - index < count; - index++) { - const nsRefPtr& metadata = metadataArray[index]; - MOZ_ASSERT(!metadata->mDeleted); - MOZ_ASSERT(metadata->mNextAutoIncrementId > 1); - - if (stmt) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(stmt->Reset())); - } else { - rv = mTransaction->mConnection->CreateStatement( - NS_LITERAL_CSTRING("UPDATE object_store " - "SET auto_increment = :") + ai + - NS_LITERAL_CSTRING(" WHERE id = :") + osid + - NS_LITERAL_CSTRING(";"), - getter_AddRefs(stmt)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = stmt->BindInt64ByName(osid, metadata->mCommonMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(ai, metadata->mNextAutoIncrementId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - } - - return NS_OK; -} - -void -TransactionBase:: -CommitOp::CommitOrRollbackAutoIncrementCounts() -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(mTransaction); - - nsTArray>& metadataArray = - mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray; - - if (!metadataArray.IsEmpty()) { - bool committed = NS_SUCCEEDED(mResultCode); - - for (uint32_t count = metadataArray.Length(), index = 0; - index < count; - index++) { - nsRefPtr& metadata = metadataArray[index]; - - if (committed) { - metadata->mComittedAutoIncrementId = metadata->mNextAutoIncrementId; - } else { - metadata->mNextAutoIncrementId = metadata->mComittedAutoIncrementId; - } - } - } -} - -NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp, nsRunnable) - -NS_IMETHODIMP -TransactionBase:: -CommitOp::Run() -{ - MOZ_ASSERT(mTransaction); - - PROFILER_LABEL("IndexedDB", - "CommitOp::Run", - js::ProfileEntry::Category::STORAGE); - - nsCOMPtr& connection = mTransaction->mConnection; - - if (!connection) { - return NS_OK; - } - - AssertIsOnTransactionThread(); - - if (NS_SUCCEEDED(mResultCode) && mTransaction->mUpdateFileRefcountFunction) { - mResultCode = mTransaction-> - mUpdateFileRefcountFunction->WillCommit(connection); - } - - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = WriteAutoIncrementCounts(); - } - - if (NS_SUCCEEDED(mResultCode)) { - NS_NAMED_LITERAL_CSTRING(commit, "COMMIT TRANSACTION"); - mResultCode = connection->ExecuteSimpleSQL(commit); - - if (NS_SUCCEEDED(mResultCode)) { - if (mTransaction->mUpdateFileRefcountFunction) { - mTransaction->mUpdateFileRefcountFunction->DidCommit(); - } - } - } - - if (NS_FAILED(mResultCode)) { - if (mTransaction->mUpdateFileRefcountFunction) { - mTransaction->mUpdateFileRefcountFunction->DidAbort(); - } - - // This may fail if SQLite already rolled back the transaction so ignore any - // errors. - unused << - connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION")); - } - - CommitOrRollbackAutoIncrementCounts(); - - if (mTransaction->mUpdateFileRefcountFunction) { - NS_NAMED_LITERAL_CSTRING(functionName, "update_refcount"); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(connection->RemoveFunction(functionName))); - } - - mTransaction->ReleaseTransactionThreadObjects(); - - return NS_OK; -} - -void -TransactionBase:: -CommitOp::TransactionFinishedBeforeUnblock() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransaction); - - PROFILER_LABEL("IndexedDB", - "CommitOp::TransactionFinishedBeforeUnblock", - js::ProfileEntry::Category::STORAGE); - - if (!IsActorDestroyed()) { - mTransaction->UpdateMetadata(mResultCode); - } -} - -void -TransactionBase:: -CommitOp::TransactionFinishedAfterUnblock() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mTransaction); - - PROFILER_LABEL("IndexedDB", - "CommitOp::TransactionFinishedAfterUnblock", - js::ProfileEntry::Category::STORAGE); - - IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", - "IDBTransaction[%llu] MT Complete", - mTransaction->TransactionId(), mResultCode); - - mTransaction->ReleaseBackgroundThreadObjects(); - - if (!mTransaction->IsActorDestroyed()) { - mTransaction->SendCompleteNotification(ClampResultCode(mResultCode)); - } - - mTransaction->GetDatabase()->UnregisterTransaction(mTransaction); - - mTransaction = nullptr; - -#ifdef DEBUG - // A bit hacky but the CommitOp is not really a normal database operation - // that is tied to an actor. Do this to make our assertions happy. - NoteActorDestroyed(); -#endif -} - -NS_IMPL_ISUPPORTS(TransactionBase::UpdateRefcountFunction, mozIStorageFunction) - -NS_IMETHODIMP -TransactionBase:: -UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues, - nsIVariant** _retval) -{ - MOZ_ASSERT(aValues); - MOZ_ASSERT(_retval); - - uint32_t numEntries; - nsresult rv = aValues->GetNumEntries(&numEntries); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(numEntries == 2); - -#ifdef DEBUG - { - int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL; - MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &type1))); - - int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL; - MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(1, &type2))); - - MOZ_ASSERT(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL && - type2 == mozIStorageValueArray::VALUE_TYPE_NULL)); - } -#endif - - rv = ProcessValue(aValues, 0, eDecrement); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = ProcessValue(aValues, 1, eIncrement); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -TransactionBase:: -UpdateRefcountFunction::WillCommit(mozIStorageConnection* aConnection) -{ - MOZ_ASSERT(aConnection); - - DatabaseUpdateFunction function(aConnection, this); - - mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function); - - nsresult rv = function.ErrorCode(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = CreateJournals(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -void -TransactionBase:: -UpdateRefcountFunction::DidCommit() -{ - mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nullptr); - - if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterCommit))) { - NS_WARNING("RemoveJournals failed!"); - } -} - -void -TransactionBase:: -UpdateRefcountFunction::DidAbort() -{ - if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterAbort))) { - NS_WARNING("RemoveJournals failed!"); - } -} - -void -TransactionBase:: -UpdateRefcountFunction::StartSavepoint() -{ - MOZ_ASSERT(!mInSavepoint); - MOZ_ASSERT(!mSavepointEntriesIndex.Count()); - - mInSavepoint = true; -} - -void -TransactionBase:: -UpdateRefcountFunction::ReleaseSavepoint() -{ - MOZ_ASSERT(mInSavepoint); - - mSavepointEntriesIndex.Clear(); - mInSavepoint = false; -} - -void -TransactionBase:: -UpdateRefcountFunction::RollbackSavepoint() -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(mInSavepoint); - - struct Helper - { - static PLDHashOperator - Rollback(const uint64_t& aKey, FileInfoEntry* aValue, void* /* aUserArg */) - { - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(aValue); - - aValue->mDelta -= aValue->mSavepointDelta; - return PL_DHASH_NEXT; - } - }; - - mSavepointEntriesIndex.EnumerateRead(Helper::Rollback, nullptr); - - mInSavepoint = false; - mSavepointEntriesIndex.Clear(); -} - -nsresult -TransactionBase:: -UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues, - int32_t aIndex, - UpdateType aUpdateType) -{ - MOZ_ASSERT(aValues); - - int32_t type; - nsresult rv = aValues->GetTypeOfIndex(aIndex, &type); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (type == mozIStorageValueArray::VALUE_TYPE_NULL) { - return NS_OK; - } - - nsString ids; - rv = aValues->GetString(aIndex, ids); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsTArray fileIds; - rv = ConvertFileIdsToArray(ids, fileIds); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - for (uint32_t i = 0; i < fileIds.Length(); i++) { - int64_t id = fileIds.ElementAt(i); - - FileInfoEntry* entry; - if (!mFileInfoEntries.Get(id, &entry)) { - nsRefPtr fileInfo = mFileManager->GetFileInfo(id); - MOZ_ASSERT(fileInfo); - - entry = new FileInfoEntry(fileInfo); - mFileInfoEntries.Put(id, entry); - } - - if (mInSavepoint) { - mSavepointEntriesIndex.Put(id, entry); - } - - switch (aUpdateType) { - case eIncrement: - entry->mDelta++; - if (mInSavepoint) { - entry->mSavepointDelta++; - } - break; - case eDecrement: - entry->mDelta--; - if (mInSavepoint) { - entry->mSavepointDelta--; - } - break; - default: - MOZ_CRASH("Unknown update type!"); - } - } - - return NS_OK; -} - -nsresult -TransactionBase:: -UpdateRefcountFunction::CreateJournals() -{ - nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); - if (NS_WARN_IF(!journalDirectory)) { - return NS_ERROR_FAILURE; - } - - for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) { - int64_t id = mJournalsToCreateBeforeCommit[i]; - - nsCOMPtr file = - mFileManager->GetFileForId(journalDirectory, id); - if (NS_WARN_IF(!file)) { - return NS_ERROR_FAILURE; - } - - nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mJournalsToRemoveAfterAbort.AppendElement(id); - } - - return NS_OK; -} - -nsresult -TransactionBase:: -UpdateRefcountFunction::RemoveJournals(const nsTArray& aJournals) -{ - nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); - if (NS_WARN_IF(!journalDirectory)) { - return NS_ERROR_FAILURE; - } - - for (uint32_t index = 0; index < aJournals.Length(); index++) { - nsCOMPtr file = - mFileManager->GetFileForId(journalDirectory, aJournals[index]); - if (NS_WARN_IF(!file)) { - return NS_ERROR_FAILURE; - } - - if (NS_FAILED(file->Remove(false))) { - NS_WARNING("Failed to removed journal!"); - } - } - - return NS_OK; -} - -PLDHashOperator -TransactionBase:: -UpdateRefcountFunction::DatabaseUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg) -{ - MOZ_ASSERT(aValue); - MOZ_ASSERT(aUserArg); - - if (!aValue->mDelta) { - return PL_DHASH_NEXT; - } - - auto function = static_cast(aUserArg); - - if (!function->Update(aKey, aValue->mDelta)) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; -} - -PLDHashOperator -TransactionBase:: -UpdateRefcountFunction::FileInfoUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg) -{ - MOZ_ASSERT(aValue); - - if (aValue->mDelta) { - aValue->mFileInfo->UpdateDBRefs(aValue->mDelta); - } - - return PL_DHASH_NEXT; -} - -bool -TransactionBase::UpdateRefcountFunction:: -DatabaseUpdateFunction::Update(int64_t aId, - int32_t aDelta) -{ - nsresult rv = UpdateInternal(aId, aDelta); - if (NS_FAILED(rv)) { - mErrorCode = rv; - return false; - } - - return true; -} - -nsresult -TransactionBase::UpdateRefcountFunction:: -DatabaseUpdateFunction::UpdateInternal(int64_t aId, - int32_t aDelta) -{ - nsresult rv; - - if (!mUpdateStatement) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE file " - "SET refcount = refcount + :delta " - "WHERE id = :id" - ), getter_AddRefs(mUpdateStatement)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - mozStorageStatementScoper updateScoper(mUpdateStatement); - - rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mUpdateStatement->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int32_t rows; - rv = mConnection->GetAffectedRows(&rows); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (rows > 0) { - if (!mSelectStatement) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id " - "FROM file " - "WHERE id = :id" - ), getter_AddRefs(mSelectStatement)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - mozStorageStatementScoper selectScoper(mSelectStatement); - - rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasResult; - rv = mSelectStatement->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - // Don't have to create the journal here, we can create all at once, - // just before commit - mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId); - } - - return NS_OK; - } - - if (!mInsertStatement) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO file (id, refcount) " - "VALUES(:id, :delta)" - ), getter_AddRefs(mInsertStatement)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - mozStorageStatementScoper insertScoper(mInsertStatement); - - rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mInsertStatement->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId); - return NS_OK; -} - -TransactionBase:: -AutoSavepoint::~AutoSavepoint() -{ - if (mTransaction) { - mTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE || - mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); - - NS_WARN_IF(NS_FAILED(mTransaction->RollbackSavepoint())); - } -} - -nsresult -TransactionBase:: -AutoSavepoint::Start(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE || - aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); - MOZ_ASSERT(!mTransaction); - - nsresult rv = aTransaction->StartSavepoint(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mTransaction = aTransaction; - - return NS_OK; -} - -nsresult -TransactionBase:: -AutoSavepoint::Commit() -{ - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnTransactionThread(); - - nsresult rv = mTransaction->ReleaseSavepoint(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mTransaction = nullptr; - - return NS_OK; -} - -nsresult -VersionChangeTransactionOp::SendSuccessResult() -{ - AssertIsOnOwningThread(); - - // Nothing to send here, the API assumes that this request always succeeds. - return NS_OK; -} - -bool -VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode) -{ - AssertIsOnOwningThread(); - - // The only option here is to cause the transaction to abort. - return false; -} - -void -VersionChangeTransactionOp::Cleanup() -{ - AssertIsOnOwningThread(); - -#ifdef DEBUG - // A bit hacky but the VersionChangeTransactionOp is not generated in response - // to a child request like most other database operations. Do this to make our - // assertions happy. - NoteActorDestroyed(); -#endif - - TransactionDatabaseOperationBase::Cleanup(); -} - -nsresult -CreateObjectStoreOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "CreateObjectStoreOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement( - "INSERT INTO object_store (id, auto_increment, name, key_path) " - "VALUES (:id, :auto_increment, :name, :key_path)", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"), - mMetadata.autoIncrement() ? 1 : 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - NS_NAMED_LITERAL_CSTRING(keyPath, "key_path"); - - if (mMetadata.keyPath().IsValid()) { - nsAutoString keyPathSerialization; - mMetadata.keyPath().SerializeToString(keyPathSerialization); - - rv = stmt->BindStringByName(keyPath, keyPathSerialization); - } else { - rv = stmt->BindNullByName(keyPath); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - -#ifdef DEBUG - { - int64_t id; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - aTransaction->Connection()->GetLastInsertRowID(&id))); - MOZ_ASSERT(mMetadata.id() == id); - } -#endif - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -DeleteObjectStoreOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "DeleteObjectStoreOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement( - "DELETE FROM object_store " - "WHERE id = :id", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mMetadata->mCommonMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (mMetadata->mCommonMetadata.autoIncrement()) { - aTransaction->ForgetModifiedAutoIncrementObjectStore(mMetadata); - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -CreateIndexOp::CreateIndexOp(VersionChangeTransaction* aTransaction, - const int64_t aObjectStoreId, - const IndexMetadata& aMetadata) - : VersionChangeTransactionOp(aTransaction) - , mMetadata(aMetadata) - , mFileManager(aTransaction->GetDatabase()->GetFileManager()) - , mDatabaseId(aTransaction->DatabaseId()) - , mObjectStoreId(aObjectStoreId) -{ - MOZ_ASSERT(aObjectStoreId); - MOZ_ASSERT(aMetadata.id()); - MOZ_ASSERT(mFileManager); - MOZ_ASSERT(!mDatabaseId.IsEmpty()); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static void - CopyUniqueValues(const IndexTable& aIndexes, - Maybe& aMaybeUniqueIndexTable) - { - aMaybeUniqueIndexTable.emplace(); - - const uint32_t indexCount = aIndexes.Count(); - MOZ_ASSERT(indexCount); - - aIndexes.EnumerateRead(Enumerate, aMaybeUniqueIndexTable.ptr()); - - if (NS_WARN_IF(aMaybeUniqueIndexTable.ref().Count() != indexCount)) { - aMaybeUniqueIndexTable.reset(); - return; - } - -#ifdef DEBUG - aMaybeUniqueIndexTable.ref().MarkImmutable(); -#endif - } - - private: - static PLDHashOperator - Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure) - { - auto* uniqueIndexTable = static_cast(aClosure); - MOZ_ASSERT(uniqueIndexTable); - MOZ_ASSERT(!uniqueIndexTable->Get(aValue->mCommonMetadata.id())); - - if (NS_WARN_IF(!uniqueIndexTable->Put(aValue->mCommonMetadata.id(), - aValue->mCommonMetadata.unique(), - fallible))) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; - } - }; - - InitThreadLocals(); - - const nsRefPtr objectStoreMetadata = - aTransaction->GetMetadataForObjectStoreId(aObjectStoreId); - MOZ_ASSERT(objectStoreMetadata); - - Helper::CopyUniqueValues(objectStoreMetadata->mIndexes, - mMaybeUniqueIndexTable); -} - -unsigned int CreateIndexOp::sThreadLocalIndex = kBadThreadLocalIndex; - -// static -void -CreateIndexOp::InitThreadLocals() -{ - AssertIsOnBackgroundThread(); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static void - Destroy(void* aThreadLocal) - { - delete static_cast(aThreadLocal); - } - }; - - if (sThreadLocalIndex == kBadThreadLocalIndex) { - if (NS_WARN_IF(PR_SUCCESS != - PR_NewThreadPrivateIndex(&sThreadLocalIndex, - &Helper::Destroy))) { - return; - } - } - - MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); -} - -nsresult -CreateIndexOp::InsertDataFromObjectStore(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode()); - MOZ_ASSERT(mMaybeUniqueIndexTable); - - PROFILER_LABEL("IndexedDB", - "CreateIndexOp::InsertDataFromObjectStore", - js::ProfileEntry::Category::STORAGE); - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement( - "SELECT id, data, file_ids, key_value " - "FROM object_data " - "WHERE object_store_id = :osid", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - // Bail early if we have no data to avoid creating the runtime below. - return NS_OK; - } - - ThreadLocalJSRuntime* runtime = ThreadLocalJSRuntime::GetOrCreate(); - if (NS_WARN_IF(!runtime)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - JSContext* cx = runtime->Context(); - JSAutoRequest ar(cx); - JSAutoCompartment ac(cx, runtime->Global()); - - do { - StructuredCloneReadInfo cloneInfo; - rv = GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, mFileManager, - &cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - JS::Rooted clone(cx); - if (NS_WARN_IF(!IDBObjectStore::DeserializeIndexValue(cx, cloneInfo, - &clone))) { - return NS_ERROR_DOM_DATA_CLONE_ERR; - } - - nsTArray updateInfo; - rv = IDBObjectStore::AppendIndexUpdateInfo(mMetadata.id(), - mMetadata.keyPath(), - mMetadata.unique(), - mMetadata.multiEntry(), - cx, - clone, - updateInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int64_t objectDataId = stmt->AsInt64(0); - - Key key; - rv = key.SetFromStatement(stmt, 3); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = UpdateIndexes(aTransaction, - mMaybeUniqueIndexTable.ref(), - key, - false, - objectDataId, - updateInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -bool -CreateIndexOp::Init(TransactionBase* aTransaction) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aTransaction); - - if (NS_WARN_IF(!mMaybeUniqueIndexTable) || - NS_WARN_IF(sThreadLocalIndex == kBadThreadLocalIndex)) { - return false; - } - - return true; -} - -nsresult -CreateIndexOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "CreateIndexOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement( - "INSERT INTO object_store_index (id, name, key_path, unique_index, " - "multientry, object_store_id) " - "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoString keyPathSerialization; - mMetadata.keyPath().SerializeToString(keyPathSerialization); - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), - keyPathSerialization); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"), - mMetadata.unique() ? 1 : 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"), - mMetadata.multiEntry() ? 1 : 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - -#ifdef DEBUG - { - int64_t id; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - aTransaction->Connection()->GetLastInsertRowID(&id))); - MOZ_ASSERT(mMetadata.id() == id); - } -#endif - - rv = InsertDataFromObjectStore(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -const JSClass CreateIndexOp::ThreadLocalJSRuntime::kGlobalClass = { - "IndexedDBTransactionThreadGlobal", - JSCLASS_GLOBAL_FLAGS, - /* addProperty*/ JS_PropertyStub, - /* delProperty */ JS_DeletePropertyStub, - /* getProperty */ JS_PropertyStub, - /* setProperty */ JS_StrictPropertyStub, - /* enumerate */ JS_EnumerateStub, - /* resolve */ JS_ResolveStub, - /* convert */ JS_ConvertStub, - /* finalize */ nullptr, - /* call */ nullptr, - /* hasInstance */ nullptr, - /* construct */ nullptr, - /* trace */ JS_GlobalObjectTraceHook -}; - -// static -auto -CreateIndexOp:: -ThreadLocalJSRuntime::GetOrCreate() -> ThreadLocalJSRuntime* -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(CreateIndexOp::kBadThreadLocalIndex != - CreateIndexOp::sThreadLocalIndex); - - auto* runtime = static_cast( - PR_GetThreadPrivate(CreateIndexOp::sThreadLocalIndex)); - if (runtime) { - return runtime; - } - - nsAutoPtr newRuntime(new ThreadLocalJSRuntime()); - - if (NS_WARN_IF(!newRuntime->Init())) { - return nullptr; - } - - DebugOnly status = - PR_SetThreadPrivate(CreateIndexOp::sThreadLocalIndex, newRuntime); - MOZ_ASSERT(status == PR_SUCCESS); - - return newRuntime.forget(); -} - -bool -CreateIndexOp:: -ThreadLocalJSRuntime::Init() -{ - MOZ_ASSERT(!IsOnBackgroundThread()); - - mRuntime = JS_NewRuntime(kRuntimeHeapSize); - if (NS_WARN_IF(!mRuntime)) { - return false; - } - - // Not setting this will cause JS_CHECK_RECURSION to report false positives. - JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024); - - mContext = JS_NewContext(mRuntime, 0); - if (NS_WARN_IF(!mContext)) { - return false; - } - - JSAutoRequest ar(mContext); - - mGlobal = JS_NewGlobalObject(mContext, &kGlobalClass, nullptr, - JS::FireOnNewGlobalHook); - if (NS_WARN_IF(!mGlobal)) { - return false; - } - - return true; -} - -nsresult -DeleteIndexOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "DeleteIndexOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement( - "DELETE FROM object_store_index " - "WHERE id = :id ", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -NormalTransactionOp::SendSuccessResult() -{ - AssertIsOnOwningThread(); - - if (!IsActorDestroyed()) { - RequestResponse response; - GetResponse(response); - - MOZ_ASSERT(response.type() != RequestResponse::T__None); - - if (response.type() == RequestResponse::Tnsresult) { - MOZ_ASSERT(NS_FAILED(response.get_nsresult())); - - return response.get_nsresult(); - } - - if (NS_WARN_IF(!PBackgroundIDBRequestParent::Send__delete__(this, - response))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - - mResponseSent = true; - - return NS_OK; -} - -bool -NormalTransactionOp::SendFailureResult(nsresult aResultCode) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aResultCode)); - - bool result = false; - - if (!IsActorDestroyed()) { - result = - PBackgroundIDBRequestParent::Send__delete__(this, - ClampResultCode(aResultCode)); - } - - mResponseSent = true; - - return result; -} - -void -NormalTransactionOp::Cleanup() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent); - - TransactionDatabaseOperationBase::Cleanup(); -} - -void -NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy) -{ - AssertIsOnOwningThread(); - - NoteActorDestroyed(); -} - -ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp( - TransactionBase* aTransaction, - const RequestParams& aParams) - : NormalTransactionOp(aTransaction) - , mParams(aParams.type() == RequestParams::TObjectStoreAddParams ? - aParams.get_ObjectStoreAddParams().commonParams() : - aParams.get_ObjectStorePutParams().commonParams()) - , mGroup(aTransaction->GetDatabase()->Group()) - , mOrigin(aTransaction->GetDatabase()->Origin()) - , mPersistenceType(aTransaction->GetDatabase()->Type()) - , mOverwrite(aParams.type() == RequestParams::TObjectStorePutParams) -{ - MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreAddParams || - aParams.type() == RequestParams::TObjectStorePutParams); - - mMetadata = - aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId()); - MOZ_ASSERT(mMetadata); -} - -nsresult -ObjectStoreAddOrPutRequestOp::CopyFileData(nsIInputStream* aInputStream, - nsIOutputStream* aOutputStream) -{ - AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreAddOrPutRequestOp::CopyFileData", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - - do { - char copyBuffer[kFileCopyBufferSize]; - - uint32_t numRead; - rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead); - if (NS_WARN_IF(NS_FAILED(rv))) { - break; - } - - if (!numRead) { - break; - } - - uint32_t numWrite; - rv = aOutputStream->Write(copyBuffer, numRead, &numWrite); - if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - break; - } - - if (NS_WARN_IF(numWrite != numRead)) { - rv = NS_ERROR_FAILURE; - break; - } - } while (true); - - nsresult rv2 = aOutputStream->Flush(); - if (NS_WARN_IF(NS_FAILED(rv2))) { - return NS_SUCCEEDED(rv) ? rv2 : rv; - } - - rv2 = aOutputStream->Close(); - if (NS_WARN_IF(NS_FAILED(rv2))) { - return NS_SUCCEEDED(rv) ? rv2 : rv; - } - - return rv; -} - -bool -ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction) -{ - AssertIsOnOwningThread(); - - const nsTArray& indexUpdateInfos = - mParams.indexUpdateInfos(); - - if (!indexUpdateInfos.IsEmpty()) { - const uint32_t count = indexUpdateInfos.Length(); - - mUniqueIndexTable.emplace(); - - for (uint32_t index = 0; index < count; index++) { - const IndexUpdateInfo& updateInfo = indexUpdateInfos[index]; - - nsRefPtr indexMetadata; - MOZ_ALWAYS_TRUE(mMetadata->mIndexes.Get(updateInfo.indexId(), - getter_AddRefs(indexMetadata))); - - MOZ_ASSERT(!indexMetadata->mDeleted); - - const int64_t& indexId = indexMetadata->mCommonMetadata.id(); - const bool& unique = indexMetadata->mCommonMetadata.unique(); - - MOZ_ASSERT(indexId == updateInfo.indexId()); - MOZ_ASSERT_IF(!indexMetadata->mCommonMetadata.multiEntry(), - !mUniqueIndexTable.ref().Get(indexId)); - - if (NS_WARN_IF(!mUniqueIndexTable.ref().Put(indexId, unique, fallible))) { - return false; - } - } - } else if (mOverwrite) { - // Kinda lame... - mUniqueIndexTable.emplace(); - } - -#ifdef DEBUG - if (mUniqueIndexTable) { - mUniqueIndexTable.ref().MarkImmutable(); - } -#endif - - const nsTArray& files = mParams.files(); - - if (!files.IsEmpty()) { - const uint32_t count = files.Length(); - - if (NS_WARN_IF(!mStoredFileInfos.SetCapacity(count))) { - return false; - } - - nsRefPtr fileManager = - aTransaction->GetDatabase()->GetFileManager(); - MOZ_ASSERT(fileManager); - - for (uint32_t index = 0; index < count; index++) { - const DatabaseFileOrMutableFileId& fileOrFileId = files[index]; - MOZ_ASSERT(fileOrFileId.type() == - DatabaseFileOrMutableFileId:: - TPBackgroundIDBDatabaseFileParent || - fileOrFileId.type() == DatabaseFileOrMutableFileId::Tint64_t); - - StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(); - MOZ_ASSERT(storedFileInfo); - - switch (fileOrFileId.type()) { - case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileParent: { - storedFileInfo->mFileActor = - static_cast( - fileOrFileId.get_PBackgroundIDBDatabaseFileParent()); - MOZ_ASSERT(storedFileInfo->mFileActor); - - storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo(); - MOZ_ASSERT(storedFileInfo->mFileInfo); - - storedFileInfo->mInputStream = - storedFileInfo->mFileActor->GetInputStream(); - if (storedFileInfo->mInputStream && !mFileManager) { - mFileManager = fileManager; - } - break; - } - - case DatabaseFileOrMutableFileId::Tint64_t: - storedFileInfo->mFileInfo = - fileManager->GetFileInfo(fileOrFileId.get_int64_t()); - MOZ_ASSERT(storedFileInfo->mFileInfo); - break; - - case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileChild: - default: - MOZ_CRASH("Should never get here!"); - } - } - } - - return true; -} - -nsresult -ObjectStoreAddOrPutRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT_IF(mFileManager, !mStoredFileInfos.IsEmpty()); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreAddOrPutRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // This will be the final key we use. - Key& key = mResponse; - key = mParams.key(); - - const bool keyUnset = key.IsUnset(); - const int64_t osid = mParams.objectStoreId(); - const KeyPath& keyPath = mMetadata->mCommonMetadata.keyPath(); - - // The "|| keyUnset" here is mostly a debugging tool. If a key isn't - // specified we should never have a collision and so it shouldn't matter - // if we allow overwrite or not. By not allowing overwrite we raise - // detectable errors rather than corrupting data. - TransactionBase::CachedStatement stmt; - if (!mOverwrite || keyUnset) { - rv = aTransaction->GetCachedStatement( - "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " - "VALUES (:osid, :key_value, :data, :file_ids)", - &stmt); - } else { - rv = aTransaction->GetCachedStatement( - "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, " - "file_ids) " - "VALUES (:osid, :key_value, :data, :file_ids)", - &stmt); - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!keyUnset || mMetadata->mCommonMetadata.autoIncrement(), - "Should have key unless autoIncrement"); - - int64_t autoIncrementNum = 0; - - if (mMetadata->mCommonMetadata.autoIncrement()) { - if (keyUnset) { - autoIncrementNum = mMetadata->mNextAutoIncrementId; - - MOZ_ASSERT(autoIncrementNum > 0); - - if (autoIncrementNum > (1LL << 53)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - key.SetFromInteger(autoIncrementNum); - } else if (key.IsFloat() && - key.ToFloat() >= mMetadata->mNextAutoIncrementId) { - autoIncrementNum = floor(key.ToFloat()); - } - - if (keyUnset && keyPath.IsValid()) { - const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo(); - MOZ_ASSERT(cloneInfo.offsetToKeyProp()); - MOZ_ASSERT(cloneInfo.data().Length() > sizeof(uint64_t)); - MOZ_ASSERT(cloneInfo.offsetToKeyProp() <= - (cloneInfo.data().Length() - sizeof(uint64_t))); - - // Special case where someone put an object into an autoIncrement'ing - // objectStore with no key in its keyPath set. We needed to figure out - // which row id we would get above before we could set that properly. - uint8_t* keyPropPointer = - const_cast(cloneInfo.data().Elements() + - cloneInfo.offsetToKeyProp()); - uint64_t keyPropValue = - ReinterpretDoubleAsUInt64(static_cast(autoIncrementNum)); - - LittleEndian::writeUint64(keyPropPointer, keyPropValue); - } - } - - key.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); - - // Compress the bytes before adding into the database. - const char* uncompressed = - reinterpret_cast(mParams.cloneInfo().data().Elements()); - size_t uncompressedLength = mParams.cloneInfo().data().Length(); - - // We don't have a smart pointer class that calls moz_free, so we need to - // manage | compressed | manually. - { - size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); - - // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage - // expects to be able to free the adopted pointer with NS_Free. - char* compressed = static_cast(moz_malloc(compressedLength)); - if (NS_WARN_IF(!compressed)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - snappy::RawCompress(uncompressed, uncompressedLength, compressed, - &compressedLength); - - uint8_t* dataBuffer = reinterpret_cast(compressed); - size_t dataBufferLength = compressedLength; - - // If this call succeeds, | compressed | is now owned by the statement, and - // we are no longer responsible for it. - rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, - dataBufferLength); - if (NS_WARN_IF(NS_FAILED(rv))) { - moz_free(compressed); - return rv; - } - } - - nsCOMPtr fileDirectory; - nsCOMPtr journalDirectory; - - if (mFileManager) { - fileDirectory = mFileManager->GetDirectory(); - if (NS_WARN_IF(!fileDirectory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - journalDirectory = mFileManager->EnsureJournalDirectory(); - if (NS_WARN_IF(!journalDirectory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - DebugOnly exists; - MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->Exists(&exists))); - MOZ_ASSERT(exists); - - DebugOnly isDirectory; - MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->IsDirectory(&isDirectory))); - MOZ_ASSERT(isDirectory); - - MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists))); - MOZ_ASSERT(exists); - - MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory))); - MOZ_ASSERT(isDirectory); - } - - if (!mStoredFileInfos.IsEmpty()) { - nsAutoString fileIds; - - for (uint32_t count = mStoredFileInfos.Length(), index = 0; - index < count; - index++) { - StoredFileInfo& storedFileInfo = mStoredFileInfos[index]; - MOZ_ASSERT(storedFileInfo.mFileInfo); - - const int64_t id = storedFileInfo.mFileInfo->Id(); - - nsCOMPtr inputStream; - storedFileInfo.mInputStream.swap(inputStream); - - if (inputStream) { - MOZ_ASSERT(fileDirectory); - MOZ_ASSERT(journalDirectory); - - nsCOMPtr diskFile = - mFileManager->GetFileForId(fileDirectory, id); - if (NS_WARN_IF(!diskFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - bool exists; - rv = diskFile->Exists(&exists); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (exists) { - bool isFile; - rv = diskFile->IsFile(&isFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_WARN_IF(!isFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - uint64_t inputStreamSize; - rv = inputStream->Available(&inputStreamSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - int64_t fileSize; - rv = diskFile->GetFileSize(&fileSize); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_WARN_IF(fileSize < 0)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_WARN_IF(uint64_t(fileSize) != inputStreamSize)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } else { - // Create a journal file first. - nsCOMPtr journalFile = - mFileManager->GetFileForId(journalDirectory, id); - if (NS_WARN_IF(!journalFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - // Now try to copy the stream. - nsRefPtr outputStream = - FileOutputStream::Create(mPersistenceType, - mGroup, - mOrigin, - diskFile); - if (NS_WARN_IF(!outputStream)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - rv = CopyFileData(inputStream, outputStream); - if (NS_FAILED(rv) && - NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { - IDB_REPORT_INTERNAL_ERR(); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - // Try to remove the file if the copy failed. - if (NS_FAILED(diskFile->Remove(false))) { - NS_WARNING("Failed to remove file after copying failed!"); - } - return rv; - } - - storedFileInfo.mCopiedSuccessfully = true; - } - } - - if (index) { - fileIds.Append(' '); - } - fileIds.AppendInt(id); - } - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - rv = stmt->Execute(); - if (rv == NS_ERROR_STORAGE_CONSTRAINT) { - MOZ_ASSERT(!keyUnset, "Generated key had a collision!"); - return rv; - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int64_t objectDataId; - rv = aTransaction->Connection()->GetLastInsertRowID(&objectDataId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Update our indexes if needed. - if (mOverwrite || !mParams.indexUpdateInfos().IsEmpty()) { - MOZ_ASSERT(mUniqueIndexTable); - - rv = UpdateIndexes(aTransaction, - mUniqueIndexTable.ref(), - key, - mOverwrite, - objectDataId, - mParams.indexUpdateInfos()); - if (NS_FAILED(rv)) { - return rv; - } - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (autoIncrementNum) { - mMetadata->mNextAutoIncrementId = autoIncrementNum + 1; - aTransaction->NoteModifiedAutoIncrementObjectStore(mMetadata); - } - - return NS_OK; -} - -void -ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse& aResponse) -{ - AssertIsOnOwningThread(); - - if (mOverwrite) { - aResponse = ObjectStorePutResponse(mResponse); - } else { - aResponse = ObjectStoreAddResponse(mResponse); - } -} - -void -ObjectStoreAddOrPutRequestOp::Cleanup() -{ - AssertIsOnOwningThread(); - - if (!mStoredFileInfos.IsEmpty()) { - for (uint32_t count = mStoredFileInfos.Length(), index = 0; - index < count; - index++) { - StoredFileInfo& storedFileInfo = mStoredFileInfos[index]; - nsRefPtr& fileActor = storedFileInfo.mFileActor; - - MOZ_ASSERT_IF(!fileActor, !storedFileInfo.mCopiedSuccessfully); - - if (fileActor && storedFileInfo.mCopiedSuccessfully) { - fileActor->ClearInputStreamParams(); - } - } - - mStoredFileInfos.Clear(); - } - - NormalTransactionOp::Cleanup(); -} - -ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams, - bool aGetAll) - : NormalTransactionOp(aTransaction) - , mObjectStoreId(aGetAll ? - aParams.get_ObjectStoreGetAllParams().objectStoreId() : - aParams.get_ObjectStoreGetParams().objectStoreId()) - , mFileManager(aTransaction->GetDatabase()->GetFileManager()) - , mOptionalKeyRange(aGetAll ? - aParams.get_ObjectStoreGetAllParams() - .optionalKeyRange() : - OptionalKeyRange(aParams.get_ObjectStoreGetParams() - .keyRange())) - , mBackgroundParent(aTransaction->GetBackgroundParent()) - , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllParams().limit() : 1) - , mGetAll(aGetAll) -{ - MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetParams || - aParams.type() == RequestParams::TObjectStoreGetAllParams); - MOZ_ASSERT(mObjectStoreId); - MOZ_ASSERT(mFileManager); - MOZ_ASSERT_IF(!aGetAll, - mOptionalKeyRange.type() == - OptionalKeyRange::TSerializedKeyRange); - MOZ_ASSERT(mBackgroundParent); -} - -nsresult -ObjectStoreGetRequestOp::ConvertResponse( - uint32_t aIndex, - SerializedStructuredCloneReadInfo& aSerializedInfo) -{ - MOZ_ASSERT(aIndex < mResponse.Length()); - - StructuredCloneReadInfo& info = mResponse[aIndex]; - - info.mData.SwapElements(aSerializedInfo.data()); - - FallibleTArray blobs; - FallibleTArray fileInfos; - nsresult rv = ConvertBlobsToActors(mBackgroundParent, - mFileManager, - info.mFiles, - blobs, - fileInfos); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(aSerializedInfo.blobsParent().IsEmpty()); - MOZ_ASSERT(aSerializedInfo.fileInfos().IsEmpty()); - - aSerializedInfo.blobsParent().SwapElements(blobs); - aSerializedInfo.fileInfos().SwapElements(fileInfos); - - return NS_OK; -} - -nsresult -ObjectStoreGetRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT_IF(!mGetAll, - mOptionalKeyRange.type() == - OptionalKeyRange::TSerializedKeyRange); - MOZ_ASSERT_IF(!mGetAll, mLimit == 1); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreGetRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool hasKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - nsAutoCString keyRangeClause; - if (hasKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - NS_LITERAL_CSTRING("key_value"), - keyRangeClause); - } - - nsCString limitClause; - if (mLimit) { - limitClause.AssignLiteral(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - nsCString query = - NS_LITERAL_CSTRING("SELECT data, file_ids " - "FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause + - NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + - limitClause; - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(); - if (NS_WARN_IF(!cloneInfo)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, mFileManager, - cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); - - return NS_OK; -} - -void -ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse) -{ - MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit); - - if (mGetAll) { - aResponse = ObjectStoreGetAllResponse(); - - if (!mResponse.IsEmpty()) { - FallibleTArray fallibleCloneInfos; - if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length()))) { - aResponse = NS_ERROR_OUT_OF_MEMORY; - return; - } - - for (uint32_t count = mResponse.Length(), index = 0; - index < count; - index++) { - nsresult rv = ConvertResponse(index, fallibleCloneInfos[index]); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResponse = rv; - return; - } - } - - nsTArray& cloneInfos = - aResponse.get_ObjectStoreGetAllResponse().cloneInfos(); - - fallibleCloneInfos.SwapElements(cloneInfos); - } - - return; - } - - aResponse = ObjectStoreGetResponse(); - - if (!mResponse.IsEmpty()) { - SerializedStructuredCloneReadInfo& serializedInfo = - aResponse.get_ObjectStoreGetResponse().cloneInfo(); - - nsresult rv = ConvertResponse(0, serializedInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResponse = rv; - } - } -} - -nsresult -ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreGetAllKeysRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool hasKeyRange = - mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange; - - nsAutoCString keyRangeClause; - if (hasKeyRange) { - GetBindingClauseForKeyRange( - mParams.optionalKeyRange().get_SerializedKeyRange(), - NS_LITERAL_CSTRING("key_value"), - keyRangeClause); - } - - nsAutoCString limitClause; - if (uint32_t limit = mParams.limit()) { - limitClause.AssignLiteral(" LIMIT "); - limitClause.AppendInt(limit); - } - - nsCString query = - NS_LITERAL_CSTRING("SELECT key_value " - "FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause + - NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + - limitClause; - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mParams.objectStoreId()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasKeyRange) { - rv = BindKeyRangeToStatement( - mParams.optionalKeyRange().get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - Key* key = mResponse.AppendElement(); - if (NS_WARN_IF(!key)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = key->SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -void -ObjectStoreGetAllKeysRequestOp::GetResponse(RequestResponse& aResponse) -{ - aResponse = ObjectStoreGetAllKeysResponse(); - - if (!mResponse.IsEmpty()) { - nsTArray& response = - aResponse.get_ObjectStoreGetAllKeysResponse().keys(); - mResponse.SwapElements(response); - } -} - -nsresult -ObjectStoreDeleteRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreDeleteRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoCString keyRangeClause; - GetBindingClauseForKeyRange(mParams.keyRange(), - NS_LITERAL_CSTRING("key_value"), - keyRangeClause); - - nsCString query = - NS_LITERAL_CSTRING("DELETE FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause; - - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mParams.objectStoreId()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = BindKeyRangeToStatement(mParams.keyRange(), stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -ObjectStoreClearRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreClearRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - TransactionBase::AutoSavepoint autoSave; - nsresult rv = autoSave.Start(aTransaction); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - TransactionBase::CachedStatement stmt; - rv = aTransaction->GetCachedStatement( - "DELETE FROM object_data " - "WHERE object_store_id = :osid", - &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mParams.objectStoreId()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->Execute(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = autoSave.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -ObjectStoreCountRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "ObjectStoreCountRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool hasKeyRange = - mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange; - - nsAutoCString keyRangeClause; - if (hasKeyRange) { - GetBindingClauseForKeyRange( - mParams.optionalKeyRange().get_SerializedKeyRange(), - NS_LITERAL_CSTRING("key_value"), - keyRangeClause); - } - - nsCString query = - NS_LITERAL_CSTRING("SELECT count(*) " - "FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause; - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mParams.objectStoreId()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasKeyRange) { - rv = BindKeyRangeToStatement( - mParams.optionalKeyRange().get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!hasResult)) { - MOZ_ASSERT(false, "This should never be possible!"); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - int64_t count = stmt->AsInt64(0); - if (NS_WARN_IF(count < 0)) { - MOZ_ASSERT(false, "This should never be possible!"); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mResponse.count() = count; - - return NS_OK; -} - -// static -already_AddRefed -IndexRequestOpBase::IndexMetadataForParams(TransactionBase* aTransaction, - const RequestParams& aParams) -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams || - aParams.type() == RequestParams::TIndexGetKeyParams || - aParams.type() == RequestParams::TIndexGetAllParams || - aParams.type() == RequestParams::TIndexGetAllKeysParams || - aParams.type() == RequestParams::TIndexCountParams); - - uint64_t objectStoreId; - uint64_t indexId; - - switch (aParams.type()) { - case RequestParams::TIndexGetParams: { - const IndexGetParams& params = aParams.get_IndexGetParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - break; - } - - case RequestParams::TIndexGetKeyParams: { - const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - break; - } - - case RequestParams::TIndexGetAllParams: { - const IndexGetAllParams& params = aParams.get_IndexGetAllParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - break; - } - - case RequestParams::TIndexGetAllKeysParams: { - const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - break; - } - - case RequestParams::TIndexCountParams: { - const IndexCountParams& params = aParams.get_IndexCountParams(); - objectStoreId = params.objectStoreId(); - indexId = params.indexId(); - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - const nsRefPtr objectStoreMetadata = - aTransaction->GetMetadataForObjectStoreId(objectStoreId); - MOZ_ASSERT(objectStoreMetadata); - - nsRefPtr indexMetadata = - aTransaction->GetMetadataForIndexId(objectStoreMetadata, indexId); - MOZ_ASSERT(indexMetadata); - - return indexMetadata.forget(); -} - -IndexGetRequestOp::IndexGetRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams, - bool aGetAll) - : IndexRequestOpBase(aTransaction, aParams) - , mFileManager(aTransaction->GetDatabase()->GetFileManager()) - , mOptionalKeyRange(aGetAll ? - aParams.get_IndexGetAllParams().optionalKeyRange() : - OptionalKeyRange(aParams.get_IndexGetParams() - .keyRange())) - , mBackgroundParent(aTransaction->GetBackgroundParent()) - , mLimit(aGetAll ? aParams.get_IndexGetAllParams().limit() : 1) - , mGetAll(aGetAll) -{ - MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams || - aParams.type() == RequestParams::TIndexGetAllParams); - MOZ_ASSERT(mFileManager); - MOZ_ASSERT_IF(!aGetAll, - mOptionalKeyRange.type() == - OptionalKeyRange::TSerializedKeyRange); - MOZ_ASSERT(mBackgroundParent); -} - -nsresult -IndexGetRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT_IF(!mGetAll, - mOptionalKeyRange.type() == - OptionalKeyRange::TSerializedKeyRange); - MOZ_ASSERT_IF(!mGetAll, mLimit == 1); - - PROFILER_LABEL("IndexedDB", - "IndexGetRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool hasKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - nsCString indexTable; - if (mMetadata->mCommonMetadata.unique()) { - indexTable.AssignLiteral("unique_index_data "); - } - else { - indexTable.AssignLiteral("index_data "); - } - - nsAutoCString keyRangeClause; - if (hasKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - NS_LITERAL_CSTRING("value"), - keyRangeClause); - } - - nsCString limitClause; - if (mLimit) { - limitClause.AssignLiteral(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - nsCString query = - NS_LITERAL_CSTRING("SELECT data, file_ids " - "FROM object_data " - "INNER JOIN ") + - indexTable + - NS_LITERAL_CSTRING("AS index_table " - "ON object_data.id = index_table.object_data_id " - "WHERE index_id = :index_id") + - keyRangeClause + - limitClause; - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mMetadata->mCommonMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(); - if (NS_WARN_IF(!cloneInfo)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, mFileManager, - cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); - - return NS_OK; -} - -void -IndexGetRequestOp::GetResponse(RequestResponse& aResponse) -{ - MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); - - if (mGetAll) { - aResponse = IndexGetAllResponse(); - - if (!mResponse.IsEmpty()) { - FallibleTArray fallibleCloneInfos; - if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length()))) { - aResponse = NS_ERROR_OUT_OF_MEMORY; - return; - } - - for (uint32_t count = mResponse.Length(), index = 0; - index < count; - index++) { - StructuredCloneReadInfo& info = mResponse[index]; - - SerializedStructuredCloneReadInfo& serializedInfo = - fallibleCloneInfos[index]; - - info.mData.SwapElements(serializedInfo.data()); - - FallibleTArray blobs; - FallibleTArray fileInfos; - nsresult rv = ConvertBlobsToActors(mBackgroundParent, - mFileManager, - info.mFiles, - blobs, - fileInfos); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResponse = rv; - return; - } - - MOZ_ASSERT(serializedInfo.blobsParent().IsEmpty()); - MOZ_ASSERT(serializedInfo.fileInfos().IsEmpty()); - - serializedInfo.blobsParent().SwapElements(blobs); - serializedInfo.fileInfos().SwapElements(fileInfos); - } - - nsTArray& cloneInfos = - aResponse.get_IndexGetAllResponse().cloneInfos(); - - fallibleCloneInfos.SwapElements(cloneInfos); - } - - return; - } - - aResponse = IndexGetResponse(); - - if (!mResponse.IsEmpty()) { - StructuredCloneReadInfo& info = mResponse[0]; - - SerializedStructuredCloneReadInfo& serializedInfo = - aResponse.get_IndexGetResponse().cloneInfo(); - - info.mData.SwapElements(serializedInfo.data()); - - FallibleTArray blobs; - FallibleTArray fileInfos; - nsresult rv = - ConvertBlobsToActors(mBackgroundParent, - mFileManager, - info.mFiles, - blobs, - fileInfos); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResponse = rv; - return; - } - - MOZ_ASSERT(serializedInfo.blobsParent().IsEmpty()); - MOZ_ASSERT(serializedInfo.fileInfos().IsEmpty()); - - serializedInfo.blobsParent().SwapElements(blobs); - serializedInfo.fileInfos().SwapElements(fileInfos); - } -} - -IndexGetKeyRequestOp::IndexGetKeyRequestOp(TransactionBase* aTransaction, - const RequestParams& aParams, - bool aGetAll) - : IndexRequestOpBase(aTransaction, aParams) - , mOptionalKeyRange(aGetAll ? - aParams.get_IndexGetAllKeysParams().optionalKeyRange() : - OptionalKeyRange(aParams.get_IndexGetKeyParams() - .keyRange())) - , mLimit(aGetAll ? aParams.get_IndexGetAllKeysParams().limit() : 1) - , mGetAll(aGetAll) -{ - MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetKeyParams || - aParams.type() == RequestParams::TIndexGetAllKeysParams); - MOZ_ASSERT_IF(!aGetAll, - mOptionalKeyRange.type() == - OptionalKeyRange::TSerializedKeyRange); -} - -nsresult -IndexGetKeyRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT_IF(!mGetAll, - mOptionalKeyRange.type() == - OptionalKeyRange::TSerializedKeyRange); - MOZ_ASSERT_IF(!mGetAll, mLimit == 1); - - PROFILER_LABEL("IndexedDB", - "IndexGetKeyRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool hasKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - nsCString indexTable; - if (mMetadata->mCommonMetadata.unique()) { - indexTable.AssignLiteral("unique_index_data "); - } - else { - indexTable.AssignLiteral("index_data "); - } - - nsAutoCString keyRangeClause; - if (hasKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - NS_LITERAL_CSTRING("value"), - keyRangeClause); - } - - nsCString limitClause; - if (mLimit) { - limitClause.AssignLiteral(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - nsCString query = - NS_LITERAL_CSTRING("SELECT object_data_key " - "FROM ") + - indexTable + - NS_LITERAL_CSTRING("WHERE index_id = :index_id") + - keyRangeClause + - limitClause; - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mMetadata->mCommonMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - Key* key = mResponse.AppendElement(); - if (NS_WARN_IF(!key)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = key->SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); - - return NS_OK; -} - -void -IndexGetKeyRequestOp::GetResponse(RequestResponse& aResponse) -{ - MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); - - if (mGetAll) { - aResponse = IndexGetAllKeysResponse(); - - if (!mResponse.IsEmpty()) { - mResponse.SwapElements(aResponse.get_IndexGetAllKeysResponse().keys()); - } - - return; - } - - aResponse = IndexGetKeyResponse(); - - if (!mResponse.IsEmpty()) { - aResponse.get_IndexGetKeyResponse().key() = Move(mResponse[0]); - } -} - -nsresult -IndexCountRequestOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - - PROFILER_LABEL("IndexedDB", - "IndexCountRequestOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool hasKeyRange = - mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange; - - nsCString indexTable; - if (mMetadata->mCommonMetadata.unique()) { - indexTable.AssignLiteral("unique_index_data "); - } - else { - indexTable.AssignLiteral("index_data "); - } - - nsAutoCString keyRangeClause; - if (hasKeyRange) { - GetBindingClauseForKeyRange( - mParams.optionalKeyRange().get_SerializedKeyRange(), - NS_LITERAL_CSTRING("value"), - keyRangeClause); - } - - nsCString query = - NS_LITERAL_CSTRING("SELECT count(*) " - "FROM ") + - indexTable + - NS_LITERAL_CSTRING("WHERE index_id = :index_id") + - keyRangeClause; - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mMetadata->mCommonMetadata.id()); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (hasKeyRange) { - rv = BindKeyRangeToStatement( - mParams.optionalKeyRange().get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (NS_WARN_IF(!hasResult)) { - MOZ_ASSERT(false, "This should never be possible!"); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - int64_t count = stmt->AsInt64(0); - if (NS_WARN_IF(count < 0)) { - MOZ_ASSERT(false, "This should never be possible!"); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mResponse.count() = count; - - return NS_OK; -} - -bool -Cursor:: -CursorOpBase::SendFailureResult(nsresult aResultCode) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aResultCode)); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this); - MOZ_ASSERT(!mResponseSent); - - if (!IsActorDestroyed()) { - mResponse = ClampResultCode(aResultCode); - - mCursor->SendResponseInternal(mResponse, mFiles); - } - - mResponseSent = true; - return false; -} - -void -Cursor:: -CursorOpBase::Cleanup() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent); - - mCursor = nullptr; - -#ifdef DEBUG - // A bit hacky but the CursorOp request is not generated in response to a - // child request like most other database operations. Do this to make our - // assertions happy. - NoteActorDestroyed(); -#endif - - TransactionDatabaseOperationBase::Cleanup(); -} - -void -Cursor:: -OpenOp::GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aKey); - MOZ_ASSERT(aKey->IsUnset()); - MOZ_ASSERT(aOpen); - if (mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) { - const SerializedKeyRange& range = - mOptionalKeyRange.get_SerializedKeyRange(); - if (range.isOnly()) { - *aKey = range.lower(); - *aOpen = false; - } else { - *aKey = aLowerBound ? range.lower() : range.upper(); - *aOpen = aLowerBound ? range.lowerOpen() : range.upperOpen(); - } - } else { - *aOpen = false; - } -} - -nsresult -Cursor:: -OpenOp::DoObjectStoreDatabaseWork(TransactionBase* aTransaction) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mType == OpenCursorParams::TObjectStoreOpenCursorParams); - MOZ_ASSERT(mCursor->mObjectStoreId); - MOZ_ASSERT(mCursor->mFileManager); - - PROFILER_LABEL("IndexedDB", - "Cursor::OpenOp::DoObjectStoreDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool usingKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); - NS_NAMED_LITERAL_CSTRING(id, "id"); - NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); - - nsCString queryStart = - NS_LITERAL_CSTRING("SELECT ") + - keyValue + - NS_LITERAL_CSTRING(", data, file_ids " - "FROM object_data " - "WHERE object_store_id = :") + - id; - - nsAutoCString keyRangeClause; - if (usingKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - keyValue, - keyRangeClause); - } - - nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; - switch (mCursor->mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause.AppendLiteral(" ASC"); - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - directionClause.AppendLiteral(" DESC"); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - nsCString firstQuery = - queryStart + - keyRangeClause + - directionClause + - openLimit + - NS_LITERAL_CSTRING("1"); - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (usingKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - mResponse = void_t(); - return NS_OK; - } - - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - StructuredCloneReadInfo cloneInfo; - rv = GetStructuredCloneReadInfoFromStatement(stmt, - 1, - 2, - mCursor->mFileManager, - &cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Now we need to make the query to get the next match. - keyRangeClause.Truncate(); - nsAutoCString continueToKeyRangeClause; - - NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mCursor->mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: { - Key upper; - bool open; - GetRangeKeyInfo(false, &upper, &open); - AppendConditionClause(keyValue, currentKey, false, false, - keyRangeClause); - AppendConditionClause(keyValue, currentKey, false, true, - continueToKeyRangeClause); - if (usingKeyRange && !upper.IsUnset()) { - AppendConditionClause(keyValue, rangeKey, true, !open, keyRangeClause); - AppendConditionClause(keyValue, rangeKey, true, !open, - continueToKeyRangeClause); - mCursor->mRangeKey = upper; - } - break; - } - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: { - Key lower; - bool open; - GetRangeKeyInfo(true, &lower, &open); - AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); - AppendConditionClause(keyValue, currentKey, true, true, - continueToKeyRangeClause); - if (usingKeyRange && !lower.IsUnset()) { - AppendConditionClause(keyValue, rangeKey, false, !open, keyRangeClause); - AppendConditionClause(keyValue, rangeKey, false, !open, - continueToKeyRangeClause); - mCursor->mRangeKey = lower; - } - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - mCursor->mContinueQuery = - queryStart + - keyRangeClause + - directionClause + - openLimit; - - mCursor->mContinueToQuery = - queryStart + - continueToKeyRangeClause + - directionClause + - openLimit; - - mResponse = ObjectStoreCursorResponse(); - - auto& response = mResponse.get_ObjectStoreCursorResponse(); - response.cloneInfo().data().SwapElements(cloneInfo.mData); - response.key() = mCursor->mKey; - - mFiles.SwapElements(cloneInfo.mFiles); - - return NS_OK; -} - -nsresult -Cursor:: -OpenOp::DoObjectStoreKeyDatabaseWork(TransactionBase* aTransaction) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mType == - OpenCursorParams::TObjectStoreOpenKeyCursorParams); - MOZ_ASSERT(mCursor->mObjectStoreId); - - PROFILER_LABEL("IndexedDB", - "Cursor::OpenOp::DoObjectStoreKeyDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool usingKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); - NS_NAMED_LITERAL_CSTRING(id, "id"); - NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); - - nsCString queryStart = - NS_LITERAL_CSTRING("SELECT ") + - keyValue + - NS_LITERAL_CSTRING(" FROM object_data " - "WHERE object_store_id = :") + - id; - - nsAutoCString keyRangeClause; - if (usingKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - keyValue, - keyRangeClause); - } - - nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; - switch (mCursor->mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause.AppendLiteral(" ASC"); - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - directionClause.AppendLiteral(" DESC"); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - nsCString firstQuery = - queryStart + - keyRangeClause + - directionClause + - openLimit + - NS_LITERAL_CSTRING("1"); - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (usingKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - mResponse = void_t(); - return NS_OK; - } - - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Now we need to make the query to get the next match. - keyRangeClause.Truncate(); - nsAutoCString continueToKeyRangeClause; - - NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mCursor->mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: { - Key upper; - bool open; - GetRangeKeyInfo(false, &upper, &open); - AppendConditionClause(keyValue, currentKey, false, false, - keyRangeClause); - AppendConditionClause(keyValue, currentKey, false, true, - continueToKeyRangeClause); - if (usingKeyRange && !upper.IsUnset()) { - AppendConditionClause(keyValue, rangeKey, true, !open, keyRangeClause); - AppendConditionClause(keyValue, rangeKey, true, !open, - continueToKeyRangeClause); - mCursor->mRangeKey = upper; - } - break; - } - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: { - Key lower; - bool open; - GetRangeKeyInfo(true, &lower, &open); - AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); - AppendConditionClause(keyValue, currentKey, true, true, - continueToKeyRangeClause); - if (usingKeyRange && !lower.IsUnset()) { - AppendConditionClause(keyValue, rangeKey, false, !open, keyRangeClause); - AppendConditionClause(keyValue, rangeKey, false, !open, - continueToKeyRangeClause); - mCursor->mRangeKey = lower; - } - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - mCursor->mContinueQuery = - queryStart + - keyRangeClause + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - continueToKeyRangeClause + - directionClause + - openLimit; - - mResponse = ObjectStoreKeyCursorResponse(mCursor->mKey); - - return NS_OK; -} - -nsresult -Cursor:: -OpenOp::DoIndexDatabaseWork(TransactionBase* aTransaction) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenCursorParams); - MOZ_ASSERT(mCursor->mObjectStoreId); - MOZ_ASSERT(mCursor->mIndexId); - - PROFILER_LABEL("IndexedDB", - "Cursor::OpenOp::DoIndexDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool usingKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - nsCString indexTable = mCursor->mUniqueIndex ? - NS_LITERAL_CSTRING("unique_index_data") : - NS_LITERAL_CSTRING("index_data"); - - NS_NAMED_LITERAL_CSTRING(value, "index_table.value"); - NS_NAMED_LITERAL_CSTRING(id, "id"); - NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); - - nsAutoCString keyRangeClause; - if (usingKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - value, - keyRangeClause); - } - - nsAutoCString directionClause = - NS_LITERAL_CSTRING(" ORDER BY ") + - value; - - switch (mCursor->mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause.AppendLiteral(" ASC, index_table.object_data_key ASC"); - break; - - case IDBCursor::PREV: - directionClause.AppendLiteral(" DESC, index_table.object_data_key DESC"); - break; - - case IDBCursor::PREV_UNIQUE: - directionClause.AppendLiteral(" DESC, index_table.object_data_key ASC"); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - nsAutoCString queryStart = - NS_LITERAL_CSTRING("SELECT index_table.value, " - "index_table.object_data_key, " - "object_data.data, " - "object_data.file_ids " - "FROM ") + - indexTable + - NS_LITERAL_CSTRING(" AS index_table " - "JOIN object_data " - "ON index_table.object_data_id = object_data.id " - "WHERE index_table.index_id = :") + - id; - - nsCString firstQuery = - queryStart + - keyRangeClause + - directionClause + - openLimit + - NS_LITERAL_CSTRING("1"); - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(id, mCursor->mIndexId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (usingKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - mResponse = void_t(); - return NS_OK; - } - - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - StructuredCloneReadInfo cloneInfo; - rv = GetStructuredCloneReadInfoFromStatement(stmt, - 2, - 3, - mCursor->mFileManager, - &cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Now we need to make the query to get the next match. - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mCursor->mDirection) { - case IDBCursor::NEXT: { - Key upper; - bool open; - GetRangeKeyInfo(false, &upper, &open); - if (usingKeyRange && !upper.IsUnset()) { - AppendConditionClause(value, rangeKey, true, !open, queryStart); - mCursor->mRangeKey = upper; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key " - "AND ( index_table.value > :current_key OR " - "index_table.object_data_key > :object_key ) " - ) + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + - directionClause + - openLimit; - break; - } - - case IDBCursor::NEXT_UNIQUE: { - Key upper; - bool open; - GetRangeKeyInfo(false, &upper, &open); - if (usingKeyRange && !upper.IsUnset()) { - AppendConditionClause(value, rangeKey, true, !open, queryStart); - mCursor->mRangeKey = upper; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value > :current_key") + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + - directionClause + - openLimit; - break; - } - - case IDBCursor::PREV: { - Key lower; - bool open; - GetRangeKeyInfo(true, &lower, &open); - if (usingKeyRange && !lower.IsUnset()) { - AppendConditionClause(value, rangeKey, false, !open, queryStart); - mCursor->mRangeKey = lower; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key " - "AND ( index_table.value < :current_key OR " - "index_table.object_data_key < :object_key ) " - ) + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + - directionClause + - openLimit; - break; - } - - case IDBCursor::PREV_UNIQUE: { - Key lower; - bool open; - GetRangeKeyInfo(true, &lower, &open); - if (usingKeyRange && !lower.IsUnset()) { - AppendConditionClause(value, rangeKey, false, !open, queryStart); - mCursor->mRangeKey = lower; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value < :current_key") + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + - directionClause + - openLimit; - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - mResponse = IndexCursorResponse(); - - auto& response = mResponse.get_IndexCursorResponse(); - response.cloneInfo().data().SwapElements(cloneInfo.mData); - response.key() = mCursor->mKey; - response.objectKey() = mCursor->mObjectKey; - - mFiles.SwapElements(cloneInfo.mFiles); - - return NS_OK; -} - -nsresult -Cursor:: -OpenOp::DoIndexKeyDatabaseWork(TransactionBase* aTransaction) -{ - AssertIsOnTransactionThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams); - MOZ_ASSERT(mCursor->mObjectStoreId); - MOZ_ASSERT(mCursor->mIndexId); - - PROFILER_LABEL("IndexedDB", - "Cursor::OpenOp::DoIndexKeyDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const bool usingKeyRange = - mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; - - nsCString table = mCursor->mUniqueIndex ? - NS_LITERAL_CSTRING("unique_index_data") : - NS_LITERAL_CSTRING("index_data"); - - NS_NAMED_LITERAL_CSTRING(value, "value"); - NS_NAMED_LITERAL_CSTRING(id, "id"); - NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); - - nsAutoCString keyRangeClause; - if (usingKeyRange) { - GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), - value, - keyRangeClause); - } - - nsAutoCString directionClause = - NS_LITERAL_CSTRING(" ORDER BY ") + - value; - - switch (mCursor->mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause.AppendLiteral(" ASC, object_data_key ASC"); - break; - - case IDBCursor::PREV: - directionClause.AppendLiteral(" DESC, object_data_key DESC"); - break; - - case IDBCursor::PREV_UNIQUE: - directionClause.AppendLiteral(" DESC, object_data_key ASC"); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - nsAutoCString queryStart = - NS_LITERAL_CSTRING("SELECT value, object_data_key " - "FROM ") + - table + - NS_LITERAL_CSTRING(" WHERE index_id = :") + - id; - - nsCString firstQuery = - queryStart + - keyRangeClause + - directionClause + - openLimit + - NS_LITERAL_CSTRING("1"); - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = stmt->BindInt64ByName(id, mCursor->mIndexId); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (usingKeyRange) { - rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), - stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - mResponse = void_t(); - return NS_OK; - } - - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Now we need to make the query to get the next match. - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mCursor->mDirection) { - case IDBCursor::NEXT: { - Key upper; - bool open; - GetRangeKeyInfo(false, &upper, &open); - if (usingKeyRange && !upper.IsUnset()) { - AppendConditionClause(value, rangeKey, true, !open, queryStart); - mCursor->mRangeKey = upper; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key " - "AND ( value > :current_key OR " - "object_data_key > :object_key )") + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key ") + - directionClause + - openLimit; - break; - } - - case IDBCursor::NEXT_UNIQUE: { - Key upper; - bool open; - GetRangeKeyInfo(false, &upper, &open); - if (usingKeyRange && !upper.IsUnset()) { - AppendConditionClause(value, rangeKey, true, !open, queryStart); - mCursor->mRangeKey = upper; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value > :current_key") + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key") + - directionClause + - openLimit; - break; - } - - case IDBCursor::PREV: { - Key lower; - bool open; - GetRangeKeyInfo(true, &lower, &open); - if (usingKeyRange && !lower.IsUnset()) { - AppendConditionClause(value, rangeKey, false, !open, queryStart); - mCursor->mRangeKey = lower; - } - - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key " - "AND ( value < :current_key OR " - "object_data_key < :object_key )") + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key ") + - directionClause + - openLimit; - break; - } - - case IDBCursor::PREV_UNIQUE: { - Key lower; - bool open; - GetRangeKeyInfo(true, &lower, &open); - if (usingKeyRange && !lower.IsUnset()) { - AppendConditionClause(value, rangeKey, false, !open, queryStart); - mCursor->mRangeKey = lower; - } - mCursor->mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value < :current_key") + - directionClause + - openLimit; - mCursor->mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key") + - directionClause + - openLimit; - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - mResponse = IndexKeyCursorResponse(); - - auto& response = mResponse.get_IndexKeyCursorResponse(); - response.key() = mCursor->mKey; - response.objectKey() = mCursor->mObjectKey; - - return NS_OK; -} - -nsresult -Cursor:: -OpenOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mContinueQuery.IsEmpty()); - MOZ_ASSERT(mCursor->mContinueToQuery.IsEmpty()); - MOZ_ASSERT(mCursor->mKey.IsUnset()); - MOZ_ASSERT(mCursor->mRangeKey.IsUnset()); - - nsresult rv; - - switch (mCursor->mType) { - case OpenCursorParams::TObjectStoreOpenCursorParams: - rv = DoObjectStoreDatabaseWork(aTransaction); - break; - - case OpenCursorParams::TObjectStoreOpenKeyCursorParams: - rv = DoObjectStoreKeyDatabaseWork(aTransaction); - break; - - case OpenCursorParams::TIndexOpenCursorParams: - rv = DoIndexDatabaseWork(aTransaction); - break; - - case OpenCursorParams::TIndexOpenKeyCursorParams: - rv = DoIndexKeyDatabaseWork(aTransaction); - break; - - default: - MOZ_CRASH("Should never get here!"); - } - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -Cursor:: -OpenOp::SendSuccessResult() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this); - MOZ_ASSERT(mResponse.type() != CursorResponse::T__None); - MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, - mCursor->mKey.IsUnset()); - MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, - mCursor->mRangeKey.IsUnset()); - MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, - mCursor->mObjectKey.IsUnset()); - - if (IsActorDestroyed()) { - return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; - } - - mCursor->SendResponseInternal(mResponse, mFiles); - - mResponseSent = true; - return NS_OK; -} - -nsresult -Cursor:: -ContinueOp::DoDatabaseWork(TransactionBase* aTransaction) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnTransactionThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mObjectStoreId); - MOZ_ASSERT(!mCursor->mContinueQuery.IsEmpty()); - MOZ_ASSERT(!mCursor->mContinueToQuery.IsEmpty()); - MOZ_ASSERT(!mCursor->mKey.IsUnset()); - - const bool isIndex = - mCursor->mType == OpenCursorParams::TIndexOpenCursorParams || - mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams; - - MOZ_ASSERT_IF(isIndex, mCursor->mIndexId); - MOZ_ASSERT_IF(isIndex, !mCursor->mObjectKey.IsUnset()); - - PROFILER_LABEL("IndexedDB", - "Cursor::ContinueOp::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - // We need to pick a query based on whether or not a key was passed to the - // continue function. If not we'll grab the the next item in the database that - // is greater than (or less than, if we're running a PREV cursor) the current - // key. If a key was passed we'll grab the next item in the database that is - // greater than (or less than, if we're running a PREV cursor) or equal to the - // key that was specified. - - nsAutoCString countString; - nsCString query; - - bool hasContinueKey = false; - uint32_t advanceCount; - - if (mParams.type() == CursorRequestParams::TContinueParams) { - // Always go to the next result. - advanceCount = 1; - countString.AppendLiteral("1"); - - if (mParams.get_ContinueParams().key().IsUnset()) { - query = mCursor->mContinueQuery + countString; - hasContinueKey = false; - } else { - query = mCursor->mContinueToQuery + countString; - hasContinueKey = true; - } - } else { - advanceCount = mParams.get_AdvanceParams().count(); - countString.AppendInt(advanceCount); - - query = mCursor->mContinueQuery + countString; - hasContinueKey = false; - } - - NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); - NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); - NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key"); - - const Key& currentKey = - hasContinueKey ? mParams.get_ContinueParams().key() : mCursor->mKey; - - const bool usingRangeKey = !mCursor->mRangeKey.IsUnset(); - - TransactionBase::CachedStatement stmt; - nsresult rv = aTransaction->GetCachedStatement(query, &stmt); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - int64_t id = isIndex ? mCursor->mIndexId : mCursor->mObjectStoreId; - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), id); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Bind current key. - rv = currentKey.BindToStatement(stmt, currentKeyName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Bind range key if it is specified. - if (usingRangeKey) { - rv = mCursor->mRangeKey.BindToStatement(stmt, rangeKeyName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - // Bind object key if duplicates are allowed and we're not continuing to a - // specific key. - if (isIndex && - !hasContinueKey && - (mCursor->mDirection == IDBCursor::NEXT || - mCursor->mDirection == IDBCursor::PREV)) { - rv = mCursor->mObjectKey.BindToStatement(stmt, objectKeyName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - bool hasResult; - for (uint32_t index = 0; index < advanceCount; index++) { - rv = stmt->ExecuteStep(&hasResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (!hasResult) { - break; - } - } - - if (!hasResult) { - mCursor->mKey.Unset(); - mCursor->mRangeKey.Unset(); - mCursor->mObjectKey.Unset(); - mResponse = void_t(); - return NS_OK; - } - - switch (mCursor->mType) { - case OpenCursorParams::TObjectStoreOpenCursorParams: { - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - StructuredCloneReadInfo cloneInfo; - rv = GetStructuredCloneReadInfoFromStatement(stmt, - 1, - 2, - mCursor->mFileManager, - &cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mResponse = ObjectStoreCursorResponse(); - - auto& response = mResponse.get_ObjectStoreCursorResponse(); - response.cloneInfo().data().SwapElements(cloneInfo.mData); - response.key() = mCursor->mKey; - - mFiles.SwapElements(cloneInfo.mFiles); - - break; - } - - case OpenCursorParams::TObjectStoreOpenKeyCursorParams: { - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mResponse = ObjectStoreKeyCursorResponse(mCursor->mKey); - - break; - } - - case OpenCursorParams::TIndexOpenCursorParams: { - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - StructuredCloneReadInfo cloneInfo; - rv = GetStructuredCloneReadInfoFromStatement(stmt, - 2, - 3, - mCursor->mFileManager, - &cloneInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mResponse = IndexCursorResponse(); - - auto& response = mResponse.get_IndexCursorResponse(); - response.cloneInfo().data().SwapElements(cloneInfo.mData); - response.key() = mCursor->mKey; - response.objectKey() = mCursor->mObjectKey; - - mFiles.SwapElements(cloneInfo.mFiles); - - break; - } - - case OpenCursorParams::TIndexOpenKeyCursorParams: { - rv = mCursor->mKey.SetFromStatement(stmt, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mResponse = IndexKeyCursorResponse(mCursor->mKey, mCursor->mObjectKey); - - break; - } - - default: - MOZ_CRASH("Should never get here!"); - } - - return NS_OK; -} - -nsresult -Cursor:: -ContinueOp::SendSuccessResult() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this); - MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, - mCursor->mKey.IsUnset()); - MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, - mCursor->mRangeKey.IsUnset()); - MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, - mCursor->mObjectKey.IsUnset()); - - if (IsActorDestroyed()) { - return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; - } - - mCursor->SendResponseInternal(mResponse, mFiles); - - mResponseSent = true; - return NS_OK; -} - -void -PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mActorDestroyed) { - unused << - PIndexedDBPermissionRequestParent::Send__delete__(this, aPermissionValue); - } -} - -void -PermissionRequestHelper::ActorDestroy(ActorDestroyReason aWhy) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; -} - -#ifdef DEBUG - -NS_IMPL_ISUPPORTS(DEBUGThreadSlower, nsIThreadObserver) - -NS_IMETHODIMP -DEBUGThreadSlower::OnDispatchedEvent(nsIThreadInternal* /* aThread */) -{ - MOZ_CRASH("Should never be called!"); -} - -NS_IMETHODIMP -DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */, - bool /* aMayWait */, - uint32_t /* aRecursionDepth */) -{ - return NS_OK; -} - -NS_IMETHODIMP -DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, - uint32_t /* aRecursionDepth */, - bool /* aEventWasProcessed */) -{ - MOZ_ASSERT(kDEBUGThreadSleepMS); - - MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == - PR_SUCCESS); - return NS_OK; -} - -#endif // DEBUG - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ActorsParent.h b/dom/indexedDB/ActorsParent.h deleted file mode 100644 index 162f6fa09a97..000000000000 --- a/dom/indexedDB/ActorsParent.h +++ /dev/null @@ -1,67 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_actorsparent_h__ -#define mozilla_dom_indexeddb_actorsparent_h__ - -template struct already_AddRefed; -class nsCString; -class nsIPrincipal; -class nsPIDOMWindow; - -namespace mozilla { -namespace ipc { - -class PBackgroundParent; - -} // namespace ipc - -namespace dom { - -class TabParent; - -namespace quota { - -class Client; - -} // namespace quota - -namespace indexedDB { - -class OptionalWindowId; -class PBackgroundIDBFactoryParent; -class PIndexedDBPermissionRequestParent; - -PBackgroundIDBFactoryParent* -AllocPBackgroundIDBFactoryParent(mozilla::ipc::PBackgroundParent* aManager, - const OptionalWindowId& aOptionalWindowId); - -bool -RecvPBackgroundIDBFactoryConstructor(mozilla::ipc::PBackgroundParent* aManager, - PBackgroundIDBFactoryParent* aActor, - const OptionalWindowId& aOptionalWindowId); - -bool -DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor); - -PIndexedDBPermissionRequestParent* -AllocPIndexedDBPermissionRequestParent(nsPIDOMWindow* aWindow, - nsIPrincipal* aPrincipal); - -bool -RecvPIndexedDBPermissionRequestConstructor( - PIndexedDBPermissionRequestParent* aActor); - -bool -DeallocPIndexedDBPermissionRequestParent( - PIndexedDBPermissionRequestParent* aActor); - -already_AddRefed -CreateQuotaClient(); - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_indexeddb_actorsparent_h__ diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp new file mode 100644 index 000000000000..5ee6b272d1e6 --- /dev/null +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -0,0 +1,710 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/basictypes.h" + +#include "AsyncConnectionHelper.h" + +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/storage.h" +#include "nsComponentManagerUtils.h" +#include "nsContentUtils.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" +#include "nsWrapperCacheInlines.h" + +#include "IDBEvents.h" +#include "IDBTransaction.h" +#include "IndexedDatabaseManager.h" +#include "ProfilerHelpers.h" +#include "ReportInternalError.h" +#include "TransactionThreadPool.h" + +#include "ipc/IndexedDBChild.h" +#include "ipc/IndexedDBParent.h" + +using namespace mozilla; +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::quota::QuotaManager; + +namespace { + +IDBTransaction* gCurrentTransaction = nullptr; + +const uint32_t kProgressHandlerGranularity = 1000; + +class MOZ_STACK_CLASS TransactionPoolEventTarget : public StackBasedEventTarget +{ +public: + NS_DECL_NSIEVENTTARGET + + explicit TransactionPoolEventTarget(IDBTransaction* aTransaction) + : mTransaction(aTransaction) + { } + +private: + IDBTransaction* mTransaction; +}; + +// This inline is just so that we always clear aBuffers appropriately even if +// something fails. +inline +nsresult +ConvertCloneReadInfosToArrayInternal( + JSContext* aCx, + nsTArray& aReadInfos, + JS::MutableHandle aResult) +{ + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (!array) { + IDB_WARNING("Failed to make array!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!aReadInfos.IsEmpty()) { + if (!JS_SetArrayLength(aCx, array, uint32_t(aReadInfos.Length()))) { + IDB_WARNING("Failed to set array length!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0, count = aReadInfos.Length(); index < count; + index++) { + StructuredCloneReadInfo& readInfo = aReadInfos[index]; + + JS::Rooted val(aCx); + if (!IDBObjectStore::DeserializeValue(aCx, readInfo, &val)) { + NS_WARNING("Failed to decode!"); + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + + if (!JS_SetElement(aCx, array, index, val)) { + IDB_WARNING("Failed to set array element!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aResult.setObject(*array); + return NS_OK; +} + +} // anonymous namespace + +HelperBase::~HelperBase() +{ + if (!NS_IsMainThread()) { + IDBRequest* request; + mRequest.forget(&request); + + if (request) { + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); + + if (mainThread) { + NS_ProxyRelease(mainThread, static_cast(request)); + } + } + } +} + +nsresult +HelperBase::WrapNative(JSContext* aCx, + nsISupports* aNative, + JS::MutableHandle aResult) +{ + NS_ASSERTION(aCx, "Null context!"); + NS_ASSERTION(aNative, "Null pointer!"); + NS_ASSERTION(aResult.address(), "Null pointer!"); + NS_ASSERTION(mRequest, "Null request!"); + + nsresult rv = nsContentUtils::WrapNative(aCx, aNative, aResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +void +HelperBase::ReleaseMainThreadObjects() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mRequest = nullptr; +} + +AsyncConnectionHelper::AsyncConnectionHelper(IDBDatabase* aDatabase, + IDBRequest* aRequest) +: HelperBase(aRequest), + mDatabase(aDatabase), + mResultCode(NS_OK), + mDispatched(false) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} + +AsyncConnectionHelper::AsyncConnectionHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest) +: HelperBase(aRequest), + mDatabase(aTransaction->mDatabase), + mTransaction(aTransaction), + mResultCode(NS_OK), + mDispatched(false) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} + +AsyncConnectionHelper::~AsyncConnectionHelper() +{ + if (!NS_IsMainThread()) { + IDBDatabase* database; + mDatabase.forget(&database); + + IDBTransaction* transaction; + mTransaction.forget(&transaction); + + nsCOMPtr mainThread; + NS_GetMainThread(getter_AddRefs(mainThread)); + NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); + + if (mainThread) { + if (database) { + NS_ProxyRelease(mainThread, static_cast(database)); + } + if (transaction) { + NS_ProxyRelease(mainThread, static_cast(transaction)); + } + } + } + + NS_ASSERTION(!mOldProgressHandler, "Should not have anything here!"); +} + +NS_IMPL_ISUPPORTS(AsyncConnectionHelper, nsIRunnable, + mozIStorageProgressHandler) + +NS_IMETHODIMP +AsyncConnectionHelper::Run() +{ + if (NS_IsMainThread()) { + PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "Run", + js::ProfileEntry::Category::STORAGE); + + if (mTransaction && + mTransaction->IsAborted()) { + // Always fire a "error" event with ABORT_ERR if the transaction was + // aborted, even if the request succeeded or failed with another error. + mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; + } + + IDBTransaction* oldTransaction = gCurrentTransaction; + gCurrentTransaction = mTransaction; + + ChildProcessSendResult sendResult = + IndexedDatabaseManager::IsMainProcess() ? + MaybeSendResponseToChildProcess(mResultCode) : + Success_NotSent; + + switch (sendResult) { + case Success_Sent: { + if (mRequest) { + mRequest->NotifyHelperSentResultsToChildProcess(NS_OK); + } + break; + } + + case Success_NotSent: { + if (mRequest) { + nsresult rv = mRequest->NotifyHelperCompleted(this); + if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { + mResultCode = rv; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread " + "response (rv = %lu)", + "IDBRequest[%llu] MT Done", + mRequest->GetSerialNumber(), mResultCode); + } + + // Call OnError if the database had an error or if the OnSuccess + // handler has an error. + if (NS_FAILED(mResultCode) || + NS_FAILED((mResultCode = OnSuccess()))) { + OnError(); + } + break; + } + + case Success_ActorDisconnected: { + // Nothing needs to be done here. + break; + } + + case Error: { + IDB_WARNING("MaybeSendResultsToChildProcess failed!"); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + if (mRequest) { + mRequest->NotifyHelperSentResultsToChildProcess(mResultCode); + } + break; + } + + default: + MOZ_CRASH("Unknown value for ChildProcessSendResult!"); + } + + NS_ASSERTION(gCurrentTransaction == mTransaction, "Should be unchanged!"); + gCurrentTransaction = oldTransaction; + + if (mDispatched && mTransaction) { + mTransaction->OnRequestFinished(); + } + + ReleaseMainThreadObjects(); + + NS_ASSERTION(!(mDatabase || mTransaction || mRequest), "Subclass didn't " + "call AsyncConnectionHelper::ReleaseMainThreadObjects!"); + + return NS_OK; + } + + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("AsyncConnectionHelper", "Run", + js::ProfileEntry::Category::STORAGE); + + IDB_PROFILER_MARK_IF(mRequest, + "IndexedDB Request %llu: Beginning database work", + "IDBRequest[%llu] DT Start", + mRequest->GetSerialNumber()); + + nsresult rv = NS_OK; + nsCOMPtr connection; + + if (mTransaction) { + rv = mTransaction->GetOrCreateConnection(getter_AddRefs(connection)); + if (NS_SUCCEEDED(rv)) { + NS_ASSERTION(connection, "This should never be null!"); + } + } + + bool setProgressHandler = false; + if (connection) { + rv = connection->SetProgressHandler(kProgressHandlerGranularity, this, + getter_AddRefs(mOldProgressHandler)); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetProgressHandler failed!"); + if (NS_SUCCEEDED(rv)) { + setProgressHandler = true; + } + } + + if (NS_SUCCEEDED(rv)) { + bool hasSavepoint = false; + if (mDatabase) { + QuotaManager::SetCurrentWindow(mDatabase->GetOwner()); + + // Make the first savepoint. + if (mTransaction) { + if (!(hasSavepoint = mTransaction->StartSavepoint())) { + NS_WARNING("Failed to make savepoint!"); + } + } + } + + mResultCode = DoDatabaseWork(connection); + + if (mDatabase) { + // Release or roll back the savepoint depending on the error code. + if (hasSavepoint) { + NS_ASSERTION(mTransaction, "Huh?!"); + if (NS_SUCCEEDED(mResultCode)) { + mTransaction->ReleaseSavepoint(); + } + else { + mTransaction->RollbackSavepoint(); + } + } + + // Don't unset this until we're sure that all SQLite activity has + // completed! + QuotaManager::SetCurrentWindow(nullptr); + } + } + else { + // NS_ERROR_NOT_AVAILABLE is our special code for "database is invalidated" + // and we should fail with RECOVERABLE_ERR. + if (rv == NS_ERROR_NOT_AVAILABLE) { + mResultCode = NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR; + } + else { + IDB_REPORT_INTERNAL_ERR(); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + + if (setProgressHandler) { + nsCOMPtr handler; + rv = connection->RemoveProgressHandler(getter_AddRefs(handler)); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "RemoveProgressHandler failed!"); +#ifdef DEBUG + if (NS_SUCCEEDED(rv)) { + NS_ASSERTION(SameCOMIdentity(handler, static_cast(this)), + "Mismatch!"); + } +#endif + } + + IDB_PROFILER_MARK_IF(mRequest, + "IndexedDB Request %llu: Finished database work " + "(rv = %lu)", + "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(), + mResultCode); + + return NS_DispatchToMainThread(this); +} + +NS_IMETHODIMP +AsyncConnectionHelper::OnProgress(mozIStorageConnection* aConnection, + bool* _retval) +{ + if (mDatabase && mDatabase->IsInvalidated()) { + // Someone is trying to delete the database file. Exit lightningfast! + *_retval = true; + return NS_OK; + } + + if (mOldProgressHandler) { + return mOldProgressHandler->OnProgress(aConnection, _retval); + } + + *_retval = false; + return NS_OK; +} + +nsresult +AsyncConnectionHelper::Dispatch(nsIEventTarget* aTarget) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsresult rv = Init(); + if (NS_FAILED(rv)) { + return rv; + } + + rv = aTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + if (mTransaction) { + mTransaction->OnNewRequest(); + } + + mDispatched = true; + + return NS_OK; +} + +nsresult +AsyncConnectionHelper::DispatchToTransactionPool() +{ + NS_ASSERTION(mTransaction, "Only ok to call this with a transaction!"); + TransactionPoolEventTarget target(mTransaction); + return Dispatch(&target); +} + +// static +void +AsyncConnectionHelper::SetCurrentTransaction(IDBTransaction* aTransaction) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!aTransaction || !gCurrentTransaction, + "Stepping on another transaction!"); + + gCurrentTransaction = aTransaction; +} + +// static +IDBTransaction* +AsyncConnectionHelper::GetCurrentTransaction() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return gCurrentTransaction; +} + +nsresult +AsyncConnectionHelper::Init() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return NS_OK; +} + +already_AddRefed +AsyncConnectionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) +{ + return CreateGenericEvent(mRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR), + eDoesNotBubble, eNotCancelable); +} + +nsresult +AsyncConnectionHelper::OnSuccess() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mRequest, "Null request!"); + + PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "OnSuccess", + js::ProfileEntry::Category::STORAGE); + + nsRefPtr event = CreateSuccessEvent(mRequest); + if (!event) { + IDB_WARNING("Failed to create event!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + bool dummy; + nsresult rv = mRequest->DispatchEvent(event, &dummy); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + WidgetEvent* internalEvent = event->GetInternalNSEvent(); + NS_ASSERTION(internalEvent, "This should never be null!"); + + NS_ASSERTION(!mTransaction || + mTransaction->IsOpen() || + mTransaction->IsAborted(), + "How else can this be closed?!"); + + if (internalEvent->mFlags.mExceptionHasBeenRisen && + mTransaction && + mTransaction->IsOpen()) { + rv = mTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +void +AsyncConnectionHelper::OnError() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mRequest, "Null request!"); + + PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "OnError", + js::ProfileEntry::Category::STORAGE); + + // Make an error event and fire it at the target. + nsRefPtr event = + CreateGenericEvent(mRequest, NS_LITERAL_STRING(ERROR_EVT_STR), eDoesBubble, + eCancelable); + if (!event) { + NS_ERROR("Failed to create event!"); + return; + } + + bool doDefault; + nsresult rv = mRequest->DispatchEvent(event, &doDefault); + if (NS_SUCCEEDED(rv)) { + NS_ASSERTION(!mTransaction || + mTransaction->IsOpen() || + mTransaction->IsAborted(), + "How else can this be closed?!"); + + WidgetEvent* internalEvent = event->GetInternalNSEvent(); + NS_ASSERTION(internalEvent, "This should never be null!"); + + if (internalEvent->mFlags.mExceptionHasBeenRisen && + mTransaction && + mTransaction->IsOpen() && + NS_FAILED(mTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR))) { + NS_WARNING("Failed to abort transaction!"); + } + + if (doDefault && + mTransaction && + mTransaction->IsOpen() && + NS_FAILED(mTransaction->Abort(mRequest))) { + NS_WARNING("Failed to abort transaction!"); + } + } + else { + NS_WARNING("DispatchEvent failed!"); + } +} + +nsresult +AsyncConnectionHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + aVal.setUndefined(); + return NS_OK; +} + +void +AsyncConnectionHelper::ReleaseMainThreadObjects() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mDatabase = nullptr; + mTransaction = nullptr; + + HelperBase::ReleaseMainThreadObjects(); +} + +AsyncConnectionHelper::ChildProcessSendResult +AsyncConnectionHelper::MaybeSendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + // If there's no request, there could never have been an actor, and so there + // is nothing to do. + if (!mRequest) { + return Success_NotSent; + } + + IDBTransaction* trans = GetCurrentTransaction(); + // We may not have a transaction, e.g. for deleteDatabase + if (!trans) { + return Success_NotSent; + } + + // Are we shutting down the child? + IndexedDBDatabaseParent* dbActor = trans->Database()->GetActorParent(); + if (dbActor && dbActor->IsDisconnected()) { + return Success_ActorDisconnected; + } + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + if (!actor) { + return Success_NotSent; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: Sending response to child " + "process (rv = %lu)", + "IDBRequest[%llu] MT Done", + mRequest->GetSerialNumber(), aResultCode); + + return SendResponseToChildProcess(aResultCode); +} + +nsresult +AsyncConnectionHelper::OnParentProcessRequestComplete( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + if (aResponseValue.type() == ResponseValue::Tnsresult) { + NS_ASSERTION(NS_FAILED(aResponseValue.get_nsresult()), "Huh?"); + SetError(aResponseValue.get_nsresult()); + } + else { + nsresult rv = UnpackResponseFromParentProcess(aResponseValue); + NS_ENSURE_SUCCESS(rv, rv); + } + + return Run(); +} + +// static +nsresult +AsyncConnectionHelper::ConvertToArrayAndCleanup( + JSContext* aCx, + nsTArray& aReadInfos, + JS::MutableHandle aResult) +{ + NS_ASSERTION(aCx, "Null context!"); + NS_ASSERTION(aResult.address(), "Null pointer!"); + + nsresult rv = ConvertCloneReadInfosToArrayInternal(aCx, aReadInfos, aResult); + + for (uint32_t index = 0; index < aReadInfos.Length(); index++) { + aReadInfos[index].mCloneBuffer.clear(); + } + aReadInfos.Clear(); + + return rv; +} + +NS_IMETHODIMP_(MozExternalRefCountType) +StackBasedEventTarget::AddRef() +{ + NS_NOTREACHED("Don't call me!"); + return 2; +} + +NS_IMETHODIMP_(MozExternalRefCountType) +StackBasedEventTarget::Release() +{ + NS_NOTREACHED("Don't call me!"); + return 1; +} + +NS_IMETHODIMP +StackBasedEventTarget::QueryInterface(REFNSIID aIID, + void** aInstancePtr) +{ + NS_NOTREACHED("Don't call me!"); + return NS_NOINTERFACE; +} + +NS_IMETHODIMP +ImmediateRunEventTarget::Dispatch(nsIRunnable* aRunnable, + uint32_t aFlags) +{ + NS_ASSERTION(aRunnable, "Null pointer!"); + + nsCOMPtr runnable(aRunnable); + DebugOnly rv = + runnable->Run(); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + return NS_OK; +} + +NS_IMETHODIMP +ImmediateRunEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) +{ + *aIsOnCurrentThread = true; + return NS_OK; +} + +NS_IMETHODIMP +TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable, + uint32_t aFlags) +{ + NS_ASSERTION(aRunnable, "Null pointer!"); + NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!"); + + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); + NS_ENSURE_TRUE(pool, NS_ERROR_UNEXPECTED); + + nsresult rv = pool->Dispatch(mTransaction, aRunnable, false, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +TransactionPoolEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) +{ + *aIsOnCurrentThread = false; + return NS_OK; +} + +NS_IMETHODIMP +NoDispatchEventTarget::Dispatch(nsIRunnable* aRunnable, + uint32_t aFlags) +{ + nsCOMPtr runnable = aRunnable; + return NS_OK; +} + +NS_IMETHODIMP +NoDispatchEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) +{ + *aIsOnCurrentThread = true; + return NS_OK; +} diff --git a/dom/indexedDB/AsyncConnectionHelper.h b/dom/indexedDB/AsyncConnectionHelper.h new file mode 100644 index 000000000000..e39a654a1ae8 --- /dev/null +++ b/dom/indexedDB/AsyncConnectionHelper.h @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_asyncconnectionhelper_h__ +#define mozilla_dom_indexeddb_asyncconnectionhelper_h__ + +// Only meant to be included in IndexedDB source files, not exported. +#include "DatabaseInfo.h" +#include "IndexedDatabase.h" +#include "IDBDatabase.h" +#include "IDBRequest.h" + +#include "mozIStorageProgressHandler.h" +#include "nsIEventTarget.h" +#include "nsIRunnable.h" + +class mozIStorageConnection; + +BEGIN_INDEXEDDB_NAMESPACE + +class AutoSetCurrentTransaction; +class IDBTransaction; + +namespace ipc { +class ResponseValue; +} + +// A common base class for AsyncConnectionHelper and OpenDatabaseHelper that +// IDBRequest can use. +class HelperBase : public nsIRunnable +{ + friend class IDBRequest; + +public: + + virtual nsresult GetResultCode() = 0; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) = 0; + + IDBRequest* GetRequest() const + { + return mRequest; + } + +protected: + explicit HelperBase(IDBRequest* aRequest) + : mRequest(aRequest) + { } + + virtual ~HelperBase(); + + /** + * Helper to wrap a native into a jsval. Uses the global object of the request + * to parent the native. + */ + nsresult WrapNative(JSContext* aCx, + nsISupports* aNative, + JS::MutableHandle aResult); + + /** + * Gives the subclass a chance to release any objects that must be released + * on the main thread, regardless of success or failure. Subclasses that + * implement this method *MUST* call the base class implementation as well. + */ + virtual void ReleaseMainThreadObjects(); + + nsRefPtr mRequest; +}; + +/** + * Must be subclassed. The subclass must implement DoDatabaseWork. It may then + * choose to implement OnSuccess and OnError depending on the needs of the + * subclass. If the default implementation of OnSuccess is desired then the + * subclass can implement GetSuccessResult to properly set the result of the + * success event. Call Dispatch to start the database operation. Must be created + * and Dispatched from the main thread only. Target thread may not be the main + * thread. + */ +class AsyncConnectionHelper : public HelperBase, + public mozIStorageProgressHandler +{ + friend class AutoSetCurrentTransaction; + +public: + typedef ipc::ResponseValue ResponseValue; + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + NS_DECL_MOZISTORAGEPROGRESSHANDLER + + virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread); + + // Only for transactions! + nsresult DispatchToTransactionPool(); + + void SetError(nsresult aErrorCode) + { + NS_ASSERTION(NS_FAILED(aErrorCode), "Not a failure code!"); + mResultCode = aErrorCode; + } + + static IDBTransaction* GetCurrentTransaction(); + + bool HasTransaction() const + { + return !!mTransaction; + } + + IDBTransaction* GetTransaction() const + { + return mTransaction; + } + + virtual nsresult GetResultCode() MOZ_OVERRIDE + { + return mResultCode; + } + + enum ChildProcessSendResult + { + // The result was successfully sent to the child process + Success_Sent = 0, + + // The result was not sent, because this is not an out-of-process request. + Success_NotSent, + + // The result was not sent, because the actor has been disconnected + // (if the child process has shut down or crashed). + Success_ActorDisconnected, + + // An error occurred. + Error + }; + + ChildProcessSendResult + MaybeSendResponseToChildProcess(nsresult aResultCode); + + virtual nsresult OnParentProcessRequestComplete( + const ResponseValue& aResponseValue); + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; + +protected: + AsyncConnectionHelper(IDBDatabase* aDatabase, + IDBRequest* aRequest); + + AsyncConnectionHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest); + + virtual ~AsyncConnectionHelper(); + + /** + * This is called on the main thread after Dispatch is called but before the + * runnable is actually dispatched to the database thread. Allows the subclass + * to initialize itself. + */ + virtual nsresult Init(); + + /** + * This callback is run on the database thread. + */ + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) = 0; + + /** + * This function returns the event to be dispatched at the request when + * OnSuccess is called. A subclass can override this to fire an event other + * than "success" at the request. + */ + virtual already_AddRefed CreateSuccessEvent( + mozilla::dom::EventTarget* aOwner); + + /** + * This callback is run on the main thread if DoDatabaseWork returned NS_OK. + * The default implementation fires a "success" DOM event with its target set + * to the request. Returning anything other than NS_OK from the OnSuccess + * callback will trigger the OnError callback. + */ + virtual nsresult OnSuccess(); + + /** + * This callback is run on the main thread if DoDatabaseWork or OnSuccess + * returned an error code. The default implementation fires an "error" DOM + * event with its target set to the request. + */ + virtual void OnError(); + + /** + * This function is called by the request on the main thread when script + * accesses the result property of the request. + */ + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + /** + * Gives the subclass a chance to release any objects that must be released + * on the main thread, regardless of success or failure. Subclasses that + * implement this method *MUST* call the base class implementation as well. + */ + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + /** + * Helper to make a JS array object out of an array of clone buffers. + */ + static nsresult ConvertToArrayAndCleanup( + JSContext* aCx, + nsTArray& aReadInfos, + JS::MutableHandle aResult); + + /** + * This should only be called by AutoSetCurrentTransaction. + */ + static void SetCurrentTransaction(IDBTransaction* aTransaction); + + /** + * Allows the subclass to send its results to the child process. Will only + * be called if all of the IPC infrastructure is available (there is an + * actor, the child is stil alive and hasn't begun shutting down). + */ + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) = 0; + +protected: + nsRefPtr mDatabase; + nsRefPtr mTransaction; + +private: + nsCOMPtr mOldProgressHandler; + nsresult mResultCode; + bool mDispatched; +}; + +class MOZ_STACK_CLASS StackBasedEventTarget : public nsIEventTarget +{ +public: + NS_DECL_ISUPPORTS_INHERITED +}; + +class MOZ_STACK_CLASS ImmediateRunEventTarget : public StackBasedEventTarget +{ +public: + NS_DECL_NSIEVENTTARGET +}; + +class MOZ_STACK_CLASS NoDispatchEventTarget : public StackBasedEventTarget +{ +public: + NS_DECL_NSIEVENTTARGET +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_asyncconnectionhelper_h__ diff --git a/dom/indexedDB/CheckPermissionsHelper.cpp b/dom/indexedDB/CheckPermissionsHelper.cpp new file mode 100644 index 000000000000..b5b9c73ece19 --- /dev/null +++ b/dom/indexedDB/CheckPermissionsHelper.cpp @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CheckPermissionsHelper.h" + +#include "nsIDOMWindow.h" +#include "nsILoadContext.h" +#include "nsIWebNavigation.h" +#include "nsIObserverService.h" +#include "nsIPermissionManager.h" +#include "nsIPrincipal.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIURI.h" + +#include "CheckQuotaHelper.h" +#include "nsContentUtils.h" +#include "nsNetUtil.h" +#include "nsThreadUtils.h" +#include "mozilla/Services.h" + +#include "IndexedDatabaseManager.h" + +#define PERMISSION_INDEXEDDB "indexedDB" +#define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt" +#define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response" + +// This is a little confusing, but our default behavior (UNKNOWN_ACTION) is to +// allow access without a prompt. If the "indexedDB" permission is set to +// ALLOW_ACTION then we will issue a prompt before allowing access. Otherwise +// (DENY_ACTION) we deny access. +#define PERMISSION_ALLOWED nsIPermissionManager::UNKNOWN_ACTION +#define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION +#define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION + +USING_INDEXEDDB_NAMESPACE +using namespace mozilla::services; +using mozilla::dom::quota::CheckQuotaHelper; + +namespace { + +inline +uint32_t +GetIndexedDBPermissions(nsIDOMWindow* aWindow) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + NS_ASSERTION(aWindow, "Chrome shouldn't check the permission!"); + + nsCOMPtr sop(do_QueryInterface(aWindow)); + NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION); + + NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()), + "Chrome windows shouldn't check the permission!"); + + nsCOMPtr webNav = do_GetInterface(aWindow); + nsCOMPtr loadContext = do_QueryInterface(webNav); + if (loadContext && loadContext->UsePrivateBrowsing()) { + // TODO Support private browsing indexedDB? + NS_WARNING("IndexedDB may not be used while in private browsing mode!"); + return PERMISSION_DENIED; + } + + nsCOMPtr permissionManager = GetPermissionManager(); + NS_ENSURE_TRUE(permissionManager, PERMISSION_DENIED); + + uint32_t permission; + nsresult rv = + permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(), + PERMISSION_INDEXEDDB, + &permission); + NS_ENSURE_SUCCESS(rv, PERMISSION_DENIED); + + return permission; +} + +} // anonymous namespace + +NS_IMPL_ISUPPORTS(CheckPermissionsHelper, nsIRunnable, + nsIInterfaceRequestor, + nsIObserver) + +NS_IMETHODIMP +CheckPermissionsHelper::Run() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + uint32_t permission = mHasPrompted ? + mPromptResult : + GetIndexedDBPermissions(mWindow); + + nsresult rv; + if (mHasPrompted) { + // Add permissions to the database, but only if we are in the parent + // process (if we are in the child process, we have already + // set the permission when the prompt was shown in the parent, as + // we cannot set the permission from the child). + if (permission != PERMISSION_PROMPT && + IndexedDatabaseManager::IsMainProcess()) { + NS_ASSERTION(mWindow, "Null window!"); + + nsCOMPtr sop = do_QueryInterface(mWindow); + NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); + + nsIPrincipal* windowPrincipal = sop->GetPrincipal(); + NS_ASSERTION(windowPrincipal, "Null principal!"); + + nsCOMPtr permissionManager = GetPermissionManager(); + NS_ENSURE_STATE(permissionManager); + + rv = permissionManager->AddFromPrincipal(windowPrincipal, + PERMISSION_INDEXEDDB, permission, + nsIPermissionManager::EXPIRE_NEVER, + 0); + NS_ENSURE_SUCCESS(rv, rv); + } + } + else if (permission == PERMISSION_PROMPT && mPromptAllowed) { + nsCOMPtr obs = GetObserverService(); + rv = obs->NotifyObservers(static_cast(this), + TOPIC_PERMISSIONS_PROMPT, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + nsRefPtr helper; + helper.swap(mHelper); + + nsCOMPtr window; + window.swap(mWindow); + + if (permission == PERMISSION_ALLOWED) { + // If we're running from a window then we should check the quota permission + // as well. If we don't have a window then we're opening a chrome database + // and the quota will be unlimited already. + if (window) { + nsCOMPtr sop = do_QueryInterface(window); + NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); + + nsIPrincipal* windowPrincipal = sop->GetPrincipal(); + NS_ASSERTION(windowPrincipal, "Null principal!"); + + uint32_t quotaPermission = + CheckQuotaHelper::GetQuotaPermission(windowPrincipal); + + if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) { + helper->SetUnlimitedQuotaAllowed(); + } + } + + return helper->DispatchToIOThread(); + } + + NS_ASSERTION(permission == PERMISSION_PROMPT || + permission == PERMISSION_DENIED, + "Unknown permission!"); + + helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + + return helper->RunImmediately(); +} + +NS_IMETHODIMP +CheckPermissionsHelper::GetInterface(const nsIID& aIID, + void** aResult) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (aIID.Equals(NS_GET_IID(nsIObserver))) { + return QueryInterface(aIID, aResult); + } + + if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) { + return mWindow->QueryInterface(aIID, aResult); + } + + *aResult = nullptr; + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +CheckPermissionsHelper::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!strcmp(aTopic, TOPIC_PERMISSIONS_RESPONSE), "Bad topic!"); + NS_ASSERTION(mPromptAllowed, "How did we get here?"); + + mHasPrompted = true; + + nsresult rv; + uint32_t promptResult = nsDependentString(aData).ToInteger(&rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Have to convert the permission we got from the user to our weird reversed + // permission type. + switch (promptResult) { + case nsIPermissionManager::ALLOW_ACTION: + mPromptResult = PERMISSION_ALLOWED; + break; + case nsIPermissionManager::DENY_ACTION: + mPromptResult = PERMISSION_DENIED; + break; + case nsIPermissionManager::UNKNOWN_ACTION: + mPromptResult = PERMISSION_PROMPT; + break; + + default: + NS_NOTREACHED("Unknown permission type!"); + mPromptResult = PERMISSION_DENIED; + } + + rv = NS_DispatchToCurrentThread(this); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} diff --git a/dom/indexedDB/CheckPermissionsHelper.h b/dom/indexedDB/CheckPermissionsHelper.h new file mode 100644 index 000000000000..ed6214ace671 --- /dev/null +++ b/dom/indexedDB/CheckPermissionsHelper.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_checkpermissionshelper_h__ +#define mozilla_dom_indexeddb_checkpermissionshelper_h__ + +// Only meant to be included in IndexedDB source files, not exported. +#include "OpenDatabaseHelper.h" + +#include "nsIInterfaceRequestor.h" +#include "nsIObserver.h" +#include "nsIRunnable.h" + +class nsIDOMWindow; +class nsIThread; + +BEGIN_INDEXEDDB_NAMESPACE + +class CheckPermissionsHelper MOZ_FINAL : public nsIRunnable, + public nsIInterfaceRequestor, + public nsIObserver +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIOBSERVER + + CheckPermissionsHelper(OpenDatabaseHelper* aHelper, + nsIDOMWindow* aWindow) + : mHelper(aHelper), + mWindow(aWindow), + // If we're trying to delete the database, we should never prompt the user. + // Anything that would prompt is translated to denied. + mPromptAllowed(!aHelper->mForDeletion), + mHasPrompted(false), + mPromptResult(0) + { + NS_ASSERTION(aHelper, "Null pointer!"); + NS_ASSERTION(aHelper->mPersistenceType == quota::PERSISTENCE_TYPE_PERSISTENT, + "Checking permission for non persistent databases?!"); + } + +private: + ~CheckPermissionsHelper() {} + + nsRefPtr mHelper; + nsCOMPtr mWindow; + bool mPromptAllowed; + bool mHasPrompted; + uint32_t mPromptResult; +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_checkpermissionshelper_h__ diff --git a/dom/indexedDB/Client.cpp b/dom/indexedDB/Client.cpp new file mode 100644 index 000000000000..6157ed0d2605 --- /dev/null +++ b/dom/indexedDB/Client.cpp @@ -0,0 +1,369 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Client.h" + +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/quota/UsageInfo.h" +#include "mozilla/dom/quota/Utilities.h" + +#include "IDBDatabase.h" +#include "IndexedDatabaseManager.h" +#include "TransactionThreadPool.h" +#include "nsISimpleEnumerator.h" + +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::quota::AssertIsOnIOThread; +using mozilla::dom::quota::QuotaManager; + +namespace { + +bool +GetDatabaseBaseFilename(const nsAString& aFilename, + nsAString& aDatabaseBaseFilename) +{ + NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!"); + + NS_NAMED_LITERAL_STRING(sqlite, ".sqlite"); + + if (!StringEndsWith(aFilename, sqlite)) { + return false; + } + + aDatabaseBaseFilename = + Substring(aFilename, 0, aFilename.Length() - sqlite.Length()); + + return true; +} + +} // anonymous namespace + +// This needs to be fully qualified to not confuse trace refcnt assertions. +NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client) +NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client) + +nsresult +Client::InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, + const nsACString& aOrigin, UsageInfo* aUsageInfo) +{ + AssertIsOnIOThread(); + + nsCOMPtr directory; + nsresult rv = + GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + // We need to see if there are any files in the directory already. If they + // are database files then we need to cleanup stored files (if it's needed) + // and also get the usage. + + nsAutoTArray subdirsToProcess; + nsAutoTArray, 20> unknownFiles; + nsTHashtable validSubdirs(20); + + nsCOMPtr entries; + rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && + hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr file = do_QueryInterface(entry); + NS_ENSURE_TRUE(file, NS_NOINTERFACE); + + nsString leafName; + rv = file->GetLeafName(leafName); + NS_ENSURE_SUCCESS(rv, rv); + + if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { + continue; + } + + if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { + continue; + } + + bool isDirectory; + rv = file->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + + if (isDirectory) { + if (!validSubdirs.GetEntry(leafName)) { + subdirsToProcess.AppendElement(leafName); + } + continue; + } + + nsString dbBaseFilename; + if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) { + unknownFiles.AppendElement(file); + continue; + } + + nsCOMPtr fmDirectory; + rv = directory->Clone(getter_AddRefs(fmDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = fmDirectory->Append(dbBaseFilename); + NS_ENSURE_SUCCESS(rv, rv); + + rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup, + aOrigin); + NS_ENSURE_SUCCESS(rv, rv); + + if (aUsageInfo) { + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(fileSize >= 0, "Negative size?!"); + + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); + + uint64_t usage; + rv = FileManager::GetUsage(fmDirectory, &usage); + NS_ENSURE_SUCCESS(rv, rv); + + aUsageInfo->AppendToFileUsage(usage); + } + + validSubdirs.PutEntry(dbBaseFilename); + } + NS_ENSURE_SUCCESS(rv, rv); + + for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) { + const nsString& subdir = subdirsToProcess[i]; + if (!validSubdirs.GetEntry(subdir)) { + NS_WARNING("Unknown subdirectory found!"); + return NS_ERROR_UNEXPECTED; + } + } + + for (uint32_t i = 0; i < unknownFiles.Length(); i++) { + nsCOMPtr& unknownFile = unknownFiles[i]; + + // Some temporary SQLite files could disappear, so we have to check if the + // unknown file still exists. + bool exists; + rv = unknownFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + nsString leafName; + unknownFile->GetLeafName(leafName); + + // The journal file may exists even after db has been correctly opened. + if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { + NS_WARNING("Unknown file found!"); + return NS_ERROR_UNEXPECTED; + } + } + } + + return NS_OK; +} + +nsresult +Client::GetUsageForOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, const nsACString& aOrigin, + UsageInfo* aUsageInfo) +{ + AssertIsOnIOThread(); + NS_ASSERTION(aUsageInfo, "Null pointer!"); + + nsCOMPtr directory; + nsresult rv = + GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +Client::OnOriginClearCompleted(PersistenceType aPersistenceType, + const OriginOrPatternString& aOriginOrPattern) +{ + AssertIsOnIOThread(); + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + if (mgr) { + mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern); + } +} + +void +Client::ReleaseIOThreadObjects() +{ + AssertIsOnIOThread(); + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + if (mgr) { + mgr->InvalidateAllFileManagers(); + } +} + +bool +Client::IsTransactionServiceActivated() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return !!TransactionThreadPool::Get(); +} + +void +Client::WaitForStoragesToComplete(nsTArray& aStorages, + nsIRunnable* aCallback) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!aStorages.IsEmpty(), "No storages to wait on!"); + NS_ASSERTION(aCallback, "Passed null callback!"); + + TransactionThreadPool* pool = TransactionThreadPool::Get(); + NS_ASSERTION(pool, "Should have checked if transaction service is active!"); + + nsTArray > databases(aStorages.Length()); + for (uint32_t index = 0; index < aStorages.Length(); index++) { + IDBDatabase* database = IDBDatabase::FromStorage(aStorages[index]); + if (!database) { + MOZ_CRASH(); + } + + databases.AppendElement(database); + } + + pool->WaitForDatabasesToComplete(databases, aCallback); +} + +void +Client::AbortTransactionsForStorage(nsIOfflineStorage* aStorage) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aStorage, "Passed null storage!"); + + TransactionThreadPool* pool = TransactionThreadPool::Get(); + NS_ASSERTION(pool, "Should have checked if transaction service is active!"); + + IDBDatabase* database = IDBDatabase::FromStorage(aStorage); + NS_ASSERTION(database, "This shouldn't be null!"); + + pool->AbortTransactionsForDatabase(database); +} + +bool +Client::HasTransactionsForStorage(nsIOfflineStorage* aStorage) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + TransactionThreadPool* pool = TransactionThreadPool::Get(); + NS_ASSERTION(pool, "Should have checked if transaction service is active!"); + + IDBDatabase* database = IDBDatabase::FromStorage(aStorage); + NS_ASSERTION(database, "This shouldn't be null!"); + + return pool->HasTransactionsForDatabase(database); +} + +void +Client::ShutdownTransactionService() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + TransactionThreadPool::Shutdown(); +} + +nsresult +Client::GetDirectory(PersistenceType aPersistenceType, + const nsACString& aOrigin, nsIFile** aDirectory) +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never fail!"); + + nsCOMPtr directory; + nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin, + getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(directory, "What?"); + + rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); + NS_ENSURE_SUCCESS(rv, rv); + + directory.forget(aDirectory); + return NS_OK; +} + +nsresult +Client::GetUsageForDirectoryInternal(nsIFile* aDirectory, + UsageInfo* aUsageInfo, + bool aDatabaseFiles) +{ + NS_ASSERTION(aDirectory, "Null pointer!"); + NS_ASSERTION(aUsageInfo, "Null pointer!"); + + nsCOMPtr entries; + nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!entries) { + return NS_OK; + } + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && + hasMore && !aUsageInfo->Canceled()) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr file(do_QueryInterface(entry)); + NS_ASSERTION(file, "Don't know what this is!"); + + bool isDirectory; + rv = file->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + + if (isDirectory) { + if (aDatabaseFiles) { + rv = GetUsageForDirectoryInternal(file, aUsageInfo, false); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + nsString leafName; + rv = file->GetLeafName(leafName); + NS_ENSURE_SUCCESS(rv, rv); + + if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { + NS_WARNING("Unknown directory found!"); + } + } + + continue; + } + + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(fileSize >= 0, "Negative size?!"); + + if (aDatabaseFiles) { + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); + } + else { + aUsageInfo->AppendToFileUsage(uint64_t(fileSize)); + } + } + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} diff --git a/dom/indexedDB/Client.h b/dom/indexedDB/Client.h new file mode 100644 index 000000000000..d9d580b307df --- /dev/null +++ b/dom/indexedDB/Client.h @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_client_h__ +#define mozilla_dom_indexeddb_client_h__ + +#include "IndexedDatabase.h" + +#include "mozilla/dom/quota/Client.h" + +#define JOURNAL_DIRECTORY_NAME "journals" + +BEGIN_INDEXEDDB_NAMESPACE + +class Client : public mozilla::dom::quota::Client +{ + typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString; + typedef mozilla::dom::quota::PersistenceType PersistenceType; + typedef mozilla::dom::quota::UsageInfo UsageInfo; + +public: + NS_IMETHOD_(MozExternalRefCountType) + AddRef() MOZ_OVERRIDE; + + NS_IMETHOD_(MozExternalRefCountType) + Release() MOZ_OVERRIDE; + + virtual Type + GetType() MOZ_OVERRIDE + { + return IDB; + } + + virtual nsresult + InitOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + UsageInfo* aUsageInfo) MOZ_OVERRIDE; + + virtual nsresult + GetUsageForOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + UsageInfo* aUsageInfo) MOZ_OVERRIDE; + + virtual void + OnOriginClearCompleted(PersistenceType aPersistenceType, + const OriginOrPatternString& aOriginOrPattern) + MOZ_OVERRIDE; + + virtual void + ReleaseIOThreadObjects() MOZ_OVERRIDE; + + virtual bool + IsFileServiceUtilized() MOZ_OVERRIDE + { + return true; + } + + virtual bool + IsTransactionServiceActivated() MOZ_OVERRIDE; + + virtual void + WaitForStoragesToComplete(nsTArray& aStorages, + nsIRunnable* aCallback) MOZ_OVERRIDE; + + virtual void + AbortTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; + + virtual bool + HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; + + virtual void + ShutdownTransactionService() MOZ_OVERRIDE; + +private: + ~Client() {} + + nsresult + GetDirectory(PersistenceType aPersistenceType, const nsACString& aOrigin, + nsIFile** aDirectory); + + nsresult + GetUsageForDirectoryInternal(nsIFile* aDirectory, + UsageInfo* aUsageInfo, + bool aDatabaseFiles); + + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_client_h__ diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp new file mode 100644 index 000000000000..fd7b6887e6e5 --- /dev/null +++ b/dom/indexedDB/DatabaseInfo.cpp @@ -0,0 +1,269 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DatabaseInfo.h" + +#include "nsDataHashtable.h" +#include "nsThreadUtils.h" + +USING_INDEXEDDB_NAMESPACE + +namespace { + +typedef nsDataHashtable + DatabaseHash; + +DatabaseHash* gDatabaseHash = nullptr; + +PLDHashOperator +EnumerateObjectStoreNames(const nsAString& aKey, + ObjectStoreInfo* aData, + void* aUserArg) +{ + nsTArray* array = static_cast*>(aUserArg); + if (!array->InsertElementSorted(aData->name)) { + NS_ERROR("Out of memory?"); + return PL_DHASH_STOP; + } + return PL_DHASH_NEXT; +} + +PLDHashOperator +CloneObjectStoreInfo(const nsAString& aKey, + ObjectStoreInfo* aData, + void* aUserArg) +{ + ObjectStoreInfoHash* hash = static_cast(aUserArg); + + nsRefPtr newInfo(new ObjectStoreInfo(*aData)); + + hash->Put(aKey, newInfo); + + return PL_DHASH_NEXT; +} + +} + +DatabaseInfo::~DatabaseInfo() +{ + // Clones are never in the hash. + if (!cloned) { + DatabaseInfo::Remove(id); + } +} + +ObjectStoreInfo::ObjectStoreInfo(ObjectStoreInfo& aOther) +: nextAutoIncrementId(aOther.nextAutoIncrementId), + comittedAutoIncrementId(aOther.comittedAutoIncrementId) +{ + *static_cast(this) = + static_cast(aOther); + + // Doesn't copy the refcount + MOZ_COUNT_CTOR(ObjectStoreInfo); +} + +#ifdef NS_BUILD_REFCNT_LOGGING + +IndexInfo::IndexInfo() +: id(INT64_MIN), + keyPath(0), + unique(false), + multiEntry(false) +{ + MOZ_COUNT_CTOR(IndexInfo); +} + +IndexInfo::IndexInfo(const IndexInfo& aOther) +: name(aOther.name), + id(aOther.id), + keyPath(aOther.keyPath), + unique(aOther.unique), + multiEntry(aOther.multiEntry) +{ + MOZ_COUNT_CTOR(IndexInfo); +} + +IndexInfo::~IndexInfo() +{ + MOZ_COUNT_DTOR(IndexInfo); +} + +ObjectStoreInfo::ObjectStoreInfo() +: nextAutoIncrementId(0), + comittedAutoIncrementId(0) +{ + MOZ_COUNT_CTOR(ObjectStoreInfo); +} + +ObjectStoreInfo::~ObjectStoreInfo() +{ + MOZ_COUNT_DTOR(ObjectStoreInfo); +} + +IndexUpdateInfo::IndexUpdateInfo() +: indexId(0), + indexUnique(false) +{ + MOZ_COUNT_CTOR(IndexUpdateInfo); +} + +IndexUpdateInfo::IndexUpdateInfo(const IndexUpdateInfo& aOther) +: indexId(aOther.indexId), + indexUnique(aOther.indexUnique), + value(aOther.value) +{ + MOZ_COUNT_CTOR(IndexUpdateInfo); +} + +IndexUpdateInfo::~IndexUpdateInfo() +{ + MOZ_COUNT_DTOR(IndexUpdateInfo); +} + +#endif /* NS_BUILD_REFCNT_LOGGING */ + +// static +bool +DatabaseInfo::Get(const nsACString& aId, + DatabaseInfo** aInfo) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!aId.IsEmpty(), "Bad id!"); + + if (gDatabaseHash && + gDatabaseHash->Get(aId, aInfo)) { + NS_IF_ADDREF(*aInfo); + return true; + } + return false; +} + +// static +bool +DatabaseInfo::Put(DatabaseInfo* aInfo) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aInfo, "Null pointer!"); + + if (!gDatabaseHash) { + nsAutoPtr databaseHash(new DatabaseHash()); + gDatabaseHash = databaseHash.forget(); + } + + if (gDatabaseHash->Get(aInfo->id, nullptr)) { + NS_ERROR("Already know about this database!"); + return false; + } + + gDatabaseHash->Put(aInfo->id, aInfo); + + return true; +} + +// static +void +DatabaseInfo::Remove(const nsACString& aId) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (gDatabaseHash) { + gDatabaseHash->Remove(aId); + + if (!gDatabaseHash->Count()) { + delete gDatabaseHash; + gDatabaseHash = nullptr; + } + } +} + +bool +DatabaseInfo::GetObjectStoreNames(nsTArray& aNames) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + aNames.Clear(); + if (objectStoreHash) { + objectStoreHash->EnumerateRead(EnumerateObjectStoreNames, &aNames); + } + return true; +} + +bool +DatabaseInfo::ContainsStoreName(const nsAString& aName) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return objectStoreHash && objectStoreHash->Get(aName, nullptr); +} + +ObjectStoreInfo* +DatabaseInfo::GetObjectStore(const nsAString& aName) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (objectStoreHash) { + return objectStoreHash->GetWeak(aName); + } + + return nullptr; +} + +bool +DatabaseInfo::PutObjectStore(ObjectStoreInfo* aInfo) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aInfo, "Null pointer!"); + + if (!objectStoreHash) { + nsAutoPtr hash(new ObjectStoreInfoHash()); + objectStoreHash = hash.forget(); + } + + if (objectStoreHash->Get(aInfo->name, nullptr)) { + NS_ERROR("Already have an entry for this objectstore!"); + return false; + } + + objectStoreHash->Put(aInfo->name, aInfo); + return true; +} + +void +DatabaseInfo::RemoveObjectStore(const nsAString& aName) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(GetObjectStore(aName), "Don't know about this one!"); + + if (objectStoreHash) { + objectStoreHash->Remove(aName); + } +} + +already_AddRefed +DatabaseInfo::Clone() +{ + nsRefPtr dbInfo(new DatabaseInfo()); + + dbInfo->cloned = true; + dbInfo->name = name; + dbInfo->group = group; + dbInfo->origin = origin; + dbInfo->version = version; + dbInfo->persistenceType = persistenceType; + dbInfo->id = id; + dbInfo->filePath = filePath; + dbInfo->nextObjectStoreId = nextObjectStoreId; + dbInfo->nextIndexId = nextIndexId; + + if (objectStoreHash) { + dbInfo->objectStoreHash = new ObjectStoreInfoHash(); + objectStoreHash->EnumerateRead(CloneObjectStoreInfo, + dbInfo->objectStoreHash); + } + + return dbInfo.forget(); +} diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h new file mode 100644 index 000000000000..3b9fa30b3a5d --- /dev/null +++ b/dom/indexedDB/DatabaseInfo.h @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_databaseinfo_h__ +#define mozilla_dom_indexeddb_databaseinfo_h__ + +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + +#include "mozilla/dom/quota/PersistenceType.h" +#include "nsRefPtrHashtable.h" +#include "nsHashKeys.h" + +#include "mozilla/dom/indexedDB/Key.h" +#include "mozilla/dom/indexedDB/KeyPath.h" +#include "mozilla/dom/indexedDB/IDBObjectStore.h" + +BEGIN_INDEXEDDB_NAMESPACE + +class IndexedDBDatabaseChild; +struct ObjectStoreInfo; + +typedef nsRefPtrHashtable + ObjectStoreInfoHash; + +struct DatabaseInfoGuts +{ + typedef mozilla::dom::quota::PersistenceType PersistenceType; + + DatabaseInfoGuts() + : nextObjectStoreId(1), nextIndexId(1) + { } + + bool operator==(const DatabaseInfoGuts& aOther) const + { + return this->name == aOther.name && + this->group == aOther.group && + this->origin == aOther.origin && + this->version == aOther.version && + this->persistenceType == aOther.persistenceType && + this->nextObjectStoreId == aOther.nextObjectStoreId && + this->nextIndexId == aOther.nextIndexId; + }; + + // Make sure to update ipc/SerializationHelpers.h when changing members here! + nsString name; + nsCString group; + nsCString origin; + uint64_t version; + PersistenceType persistenceType; + int64_t nextObjectStoreId; + int64_t nextIndexId; +}; + +struct DatabaseInfo MOZ_FINAL : public DatabaseInfoGuts +{ + DatabaseInfo() + : cloned(false) + { } + +private: + // Private destructor, to discourage deletion outside of Release(): + ~DatabaseInfo(); + +public: + static bool Get(const nsACString& aId, + DatabaseInfo** aInfo); + + static bool Put(DatabaseInfo* aInfo); + + static void Remove(const nsACString& aId); + + bool GetObjectStoreNames(nsTArray& aNames); + bool ContainsStoreName(const nsAString& aName); + + ObjectStoreInfo* GetObjectStore(const nsAString& aName); + + bool PutObjectStore(ObjectStoreInfo* aInfo); + + void RemoveObjectStore(const nsAString& aName); + + already_AddRefed Clone(); + + nsCString id; + nsString filePath; + bool cloned; + + nsAutoPtr objectStoreHash; + + NS_INLINE_DECL_REFCOUNTING(DatabaseInfo) +}; + +struct IndexInfo +{ +#ifdef NS_BUILD_REFCNT_LOGGING + IndexInfo(); + IndexInfo(const IndexInfo& aOther); + ~IndexInfo(); +#else + IndexInfo() + : id(INT64_MIN), keyPath(0), unique(false), multiEntry(false) { } +#endif + + bool operator==(const IndexInfo& aOther) const + { + return this->name == aOther.name && + this->id == aOther.id && + this->keyPath == aOther.keyPath && + this->unique == aOther.unique && + this->multiEntry == aOther.multiEntry; + }; + + // Make sure to update ipc/SerializationHelpers.h when changing members here! + nsString name; + int64_t id; + KeyPath keyPath; + bool unique; + bool multiEntry; +}; + +struct ObjectStoreInfoGuts +{ + ObjectStoreInfoGuts() + : id(0), keyPath(0), autoIncrement(false) + { } + + bool operator==(const ObjectStoreInfoGuts& aOther) const + { + return this->name == aOther.name && + this->id == aOther.id; + }; + + // Make sure to update ipc/SerializationHelpers.h when changing members here! + + // Constant members, can be gotten on any thread + nsString name; + int64_t id; + KeyPath keyPath; + bool autoIncrement; + + // Main-thread only members. This must *not* be touched on the database + // thread. + nsTArray indexes; +}; + +struct ObjectStoreInfo MOZ_FINAL : public ObjectStoreInfoGuts +{ +#ifdef NS_BUILD_REFCNT_LOGGING + ObjectStoreInfo(); +#else + ObjectStoreInfo() + : nextAutoIncrementId(0), comittedAutoIncrementId(0) { } +#endif + + ObjectStoreInfo(ObjectStoreInfo& aOther); + +private: + // Private destructor, to discourage deletion outside of Release(): +#ifdef NS_BUILD_REFCNT_LOGGING + ~ObjectStoreInfo(); +#else + ~ObjectStoreInfo() {} +#endif +public: + + // Database-thread members. After the ObjectStoreInfo has been initialized, + // these can *only* be touced on the database thread. + int64_t nextAutoIncrementId; + int64_t comittedAutoIncrementId; + + // This is threadsafe since the ObjectStoreInfos are created on the database + // thread but then only used from the main thread. Ideal would be if we + // could transfer ownership from the database thread to the main thread, but + // we don't have that ability yet. + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObjectStoreInfo) +}; + +struct IndexUpdateInfo +{ +#ifdef NS_BUILD_REFCNT_LOGGING + IndexUpdateInfo(); + IndexUpdateInfo(const IndexUpdateInfo& aOther); + ~IndexUpdateInfo(); +#endif + + bool operator==(const IndexUpdateInfo& aOther) const + { + return this->indexId == aOther.indexId && + this->indexUnique == aOther.indexUnique && + this->value == aOther.value; + }; + + // Make sure to update ipc/SerializationHelpers.h when changing members here! + int64_t indexId; + bool indexUnique; + Key value; +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_databaseinfo_h__ diff --git a/dom/indexedDB/FileInfo.cpp b/dom/indexedDB/FileInfo.cpp index b7ad44f9d2df..c177e5d31c2c 100644 --- a/dom/indexedDB/FileInfo.cpp +++ b/dom/indexedDB/FileInfo.cpp @@ -5,113 +5,68 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileInfo.h" - -#include "FileManager.h" -#include "IndexedDatabaseManager.h" -#include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" -#include "mozilla/Mutex.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "nsError.h" #include "nsThreadUtils.h" +#include "mozilla/dom/quota/QuotaManager.h" -namespace mozilla { -namespace dom { -namespace indexedDB { - -using namespace mozilla::dom::quota; +USING_INDEXEDDB_NAMESPACE namespace { -template -class FileInfoImpl MOZ_FINAL - : public FileInfo +class CleanupFileRunnable MOZ_FINAL : public nsIRunnable { - IdType mFileId; + ~CleanupFileRunnable() {} public: - FileInfoImpl(FileManager* aFileManager, IdType aFileId) - : FileInfo(aFileManager) - , mFileId(aFileId) - { - MOZ_ASSERT(aFileManager); - MOZ_ASSERT(aFileId > 0); - } + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + + CleanupFileRunnable(FileManager* aFileManager, int64_t aFileId); private: - ~FileInfoImpl() - { } - - virtual int64_t - Id() const MOZ_OVERRIDE - { - return int64_t(mFileId); - } -}; - -class CleanupFileRunnable MOZ_FINAL - : public nsRunnable -{ nsRefPtr mFileManager; int64_t mFileId; - -public: - static void - DoCleanup(FileManager* aFileManager, int64_t aFileId); - - CleanupFileRunnable(FileManager* aFileManager, int64_t aFileId) - : mFileManager(aFileManager) - , mFileId(aFileId) - { - MOZ_ASSERT(aFileManager); - MOZ_ASSERT(aFileId > 0); - } - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~CleanupFileRunnable() - { } - - NS_DECL_NSIRUNNABLE }; } // anonymous namespace -FileInfo::FileInfo(FileManager* aFileManager) - : mFileManager(aFileManager) -{ - MOZ_ASSERT(aFileManager); -} - -FileInfo::~FileInfo() -{ -} - // static FileInfo* FileInfo::Create(FileManager* aFileManager, int64_t aId) { - MOZ_ASSERT(aFileManager); - MOZ_ASSERT(aId > 0); + MOZ_ASSERT(aId > 0, "Wrong id!"); if (aId <= INT16_MAX) { - return new FileInfoImpl(aFileManager, aId); + return new FileInfo16(aFileManager, aId); } if (aId <= INT32_MAX) { - return new FileInfoImpl(aFileManager, aId); + return new FileInfo32(aFileManager, aId); } - return new FileInfoImpl(aFileManager, aId); + return new FileInfo64(aFileManager, aId); } void -FileInfo::GetReferences(int32_t* aRefCnt, - int32_t* aDBRefCnt, +FileInfo::GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, int32_t* aSliceRefCnt) { - MOZ_ASSERT(!IndexedDatabaseManager::IsClosed()); + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + + if (aRefCnt) { + *aRefCnt = -1; + } + + if (aDBRefCnt) { + *aDBRefCnt = -1; + } + + if (aSliceRefCnt) { + *aSliceRefCnt = -1; + } + + return; + } MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); @@ -129,31 +84,14 @@ FileInfo::GetReferences(int32_t* aRefCnt, } void -FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, - int32_t aDelta, - bool aClear) +FileInfo::UpdateReferences(mozilla::ThreadSafeAutoRefCnt& aRefCount, + int32_t aDelta, bool aClear) { - // XXX This can go away once DOM objects no longer hold FileInfo objects... - // Looking at you, IDBMutableFile... if (IndexedDatabaseManager::IsClosed()) { - MOZ_ASSERT(&aRefCount == &mRefCnt); - MOZ_ASSERT(aDelta == 1 || aDelta == -1); - MOZ_ASSERT(!aClear); - - if (aDelta > 0) { - ++aRefCount; - } else { - nsrefcnt count = --aRefCount; - if (!count) { - mRefCnt = 1; - delete this; - } - } + NS_ERROR("Shouldn't be called after shutdown!"); return; } - MOZ_ASSERT(!IndexedDatabaseManager::IsClosed()); - bool needsCleanup; { MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); @@ -179,52 +117,40 @@ FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, void FileInfo::Cleanup() { - int64_t id = Id(); + nsRefPtr cleaner = + new CleanupFileRunnable(mFileManager, Id()); // IndexedDatabaseManager is main-thread only. if (!NS_IsMainThread()) { - nsRefPtr cleaner = - new CleanupFileRunnable(mFileManager, id); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(cleaner))); + NS_DispatchToMainThread(cleaner); return; } - CleanupFileRunnable::DoCleanup(mFileManager, id); + cleaner->Run(); } -// static -void -CleanupFileRunnable::DoCleanup(FileManager* aFileManager, int64_t aFileId) +CleanupFileRunnable::CleanupFileRunnable(FileManager* aFileManager, + int64_t aFileId) +: mFileManager(aFileManager), mFileId(aFileId) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aFileManager); - MOZ_ASSERT(aFileId > 0); +} - if (NS_WARN_IF(QuotaManager::IsShuttingDown())) { - return; +NS_IMPL_ISUPPORTS(CleanupFileRunnable, + nsIRunnable) + +NS_IMETHODIMP +CleanupFileRunnable::Run() +{ + if (mozilla::dom::quota::QuotaManager::IsShuttingDown()) { + return NS_OK; } nsRefPtr mgr = IndexedDatabaseManager::Get(); MOZ_ASSERT(mgr); - if (NS_FAILED(mgr->AsyncDeleteFile(aFileManager, aFileId))) { + if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, mFileId))) { NS_WARNING("Failed to delete file asynchronously!"); } -} - -NS_IMPL_ISUPPORTS_INHERITED0(CleanupFileRunnable, nsRunnable) - -NS_IMETHODIMP -CleanupFileRunnable::Run() -{ - MOZ_ASSERT(NS_IsMainThread()); - - DoCleanup(mFileManager, mFileId); return NS_OK; } - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/FileInfo.h b/dom/indexedDB/FileInfo.h index 83d786950362..9272bd16d7c1 100644 --- a/dom/indexedDB/FileInfo.h +++ b/dom/indexedDB/FileInfo.h @@ -7,88 +7,112 @@ #ifndef mozilla_dom_indexeddb_fileinfo_h__ #define mozilla_dom_indexeddb_fileinfo_h__ -#include "nsAutoPtr.h" -#include "nsISupportsImpl.h" +#include "IndexedDatabase.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "FileManager.h" +#include "IndexedDatabaseManager.h" -class FileManager; +BEGIN_INDEXEDDB_NAMESPACE class FileInfo { friend class FileManager; +public: + explicit FileInfo(FileManager* aFileManager) + : mFileManager(aFileManager) + { } + + virtual ~FileInfo() + { } + + static + FileInfo* Create(FileManager* aFileManager, int64_t aId); + + void AddRef() + { + if (IndexedDatabaseManager::IsClosed()) { + ++mRefCnt; + } + else { + UpdateReferences(mRefCnt, 1); + } + } + + void Release() + { + if (IndexedDatabaseManager::IsClosed()) { + nsrefcnt count = --mRefCnt; + if (count == 0) { + mRefCnt = 1; + delete this; + } + } + else { + UpdateReferences(mRefCnt, -1); + } + } + + void UpdateDBRefs(int32_t aDelta) + { + UpdateReferences(mDBRefCnt, aDelta); + } + + void ClearDBRefs() + { + UpdateReferences(mDBRefCnt, 0, true); + } + + void UpdateSliceRefs(int32_t aDelta) + { + UpdateReferences(mSliceRefCnt, aDelta); + } + + void GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, + int32_t* aSliceRefCnt); + + FileManager* Manager() const + { + return mFileManager; + } + + virtual int64_t Id() const = 0; + +private: + void UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, int32_t aDelta, + bool aClear = false); + void Cleanup(); + ThreadSafeAutoRefCnt mRefCnt; ThreadSafeAutoRefCnt mDBRefCnt; ThreadSafeAutoRefCnt mSliceRefCnt; nsRefPtr mFileManager; - -public: - static - FileInfo* Create(FileManager* aFileManager, int64_t aId); - - explicit FileInfo(FileManager* aFileManager); - - void - AddRef() - { - UpdateReferences(mRefCnt, 1); - } - - void - Release() - { - UpdateReferences(mRefCnt, -1); - } - - void - UpdateDBRefs(int32_t aDelta) - { - UpdateReferences(mDBRefCnt, aDelta); - } - - void - ClearDBRefs() - { - UpdateReferences(mDBRefCnt, 0, true); - } - - void - UpdateSliceRefs(int32_t aDelta) - { - UpdateReferences(mSliceRefCnt, aDelta); - } - - void - GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, int32_t* aSliceRefCnt); - - FileManager* - Manager() const - { - return mFileManager; - } - - virtual int64_t - Id() const = 0; - -protected: - virtual ~FileInfo(); - -private: - void - UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, - int32_t aDelta, - bool aClear = false); - - void - Cleanup(); }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +#define FILEINFO_SUBCLASS(_bits) \ +class FileInfo##_bits : public FileInfo \ +{ \ +public: \ + FileInfo##_bits(FileManager* aFileManager, int##_bits##_t aId) \ + : FileInfo(aFileManager), mId(aId) \ + { } \ + \ + virtual int64_t Id() const \ + { \ + return mId; \ + } \ + \ +private: \ + int##_bits##_t mId; \ +}; + +FILEINFO_SUBCLASS(16) +FILEINFO_SUBCLASS(32) +FILEINFO_SUBCLASS(64) + +#undef FILEINFO_SUBCLASS + +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_fileinfo_h__ diff --git a/dom/indexedDB/FileManager.cpp b/dom/indexedDB/FileManager.cpp new file mode 100644 index 000000000000..c0fc7ea94525 --- /dev/null +++ b/dom/indexedDB/FileManager.cpp @@ -0,0 +1,433 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileManager.h" + +#include "mozIStorageConnection.h" +#include "mozIStorageStatement.h" +#include "nsIInputStream.h" +#include "nsISimpleEnumerator.h" + +#include "mozilla/dom/quota/Utilities.h" +#include "mozStorageCID.h" +#include "mozStorageHelper.h" + +#include "Client.h" +#include "FileInfo.h" +#include "IndexedDatabaseManager.h" +#include "OpenDatabaseHelper.h" + +#include "IndexedDatabaseInlines.h" +#include + +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::quota::AssertIsOnIOThread; + +namespace { + +PLDHashOperator +EnumerateToTArray(const uint64_t& aKey, + FileInfo* aValue, + void* aUserArg) +{ + NS_ASSERTION(aValue, "Null pointer!"); + NS_ASSERTION(aUserArg, "Null pointer!"); + + nsTArray* array = + static_cast*>(aUserArg); + + array->AppendElement(aValue); + + return PL_DHASH_NEXT; +} + +already_AddRefed +GetDirectoryFor(const nsAString& aDirectoryPath) +{ + nsCOMPtr directory = + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + NS_ENSURE_TRUE(directory, nullptr); + + nsresult rv = directory->InitWithPath(aDirectoryPath); + NS_ENSURE_SUCCESS(rv, nullptr); + + return directory.forget(); +} + +} // anonymous namespace + +nsresult +FileManager::Init(nsIFile* aDirectory, + mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(aDirectory, "Null directory!"); + NS_ASSERTION(aConnection, "Null connection!"); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + bool isDirectory; + rv = aDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + } + else { + rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aDirectory->GetPath(mDirectoryPath); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr journalDirectory; + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + bool isDirectory; + rv = journalDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + } + + rv = journalDirectory->GetPath(mJournalDirectoryPath); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id, refcount " + "FROM file" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { + int64_t id; + rv = stmt->GetInt64(0, &id); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t refcount; + rv = stmt->GetInt32(1, &refcount); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(refcount, "This shouldn't happen!"); + + nsRefPtr fileInfo = FileInfo::Create(this, id); + fileInfo->mDBRefCnt = refcount; + + mFileInfos.Put(id, fileInfo); + + mLastFileId = std::max(id, mLastFileId); + } + + return NS_OK; +} + +nsresult +FileManager::Invalidate() +{ + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + return NS_ERROR_UNEXPECTED; + } + + nsTArray fileInfos; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + + NS_ASSERTION(!mInvalidated, "Invalidate more than once?!"); + mInvalidated = true; + + fileInfos.SetCapacity(mFileInfos.Count()); + mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos); + } + + for (uint32_t i = 0; i < fileInfos.Length(); i++) { + FileInfo* fileInfo = fileInfos.ElementAt(i); + fileInfo->ClearDBRefs(); + } + + return NS_OK; +} + +already_AddRefed +FileManager::GetDirectory() +{ + return GetDirectoryFor(mDirectoryPath); +} + +already_AddRefed +FileManager::GetJournalDirectory() +{ + return GetDirectoryFor(mJournalDirectoryPath); +} + +already_AddRefed +FileManager::EnsureJournalDirectory() +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + + nsCOMPtr journalDirectory = GetDirectoryFor(mJournalDirectoryPath); + NS_ENSURE_TRUE(journalDirectory, nullptr); + + bool exists; + nsresult rv = journalDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, nullptr); + + if (exists) { + bool isDirectory; + rv = journalDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, nullptr); + NS_ENSURE_TRUE(isDirectory, nullptr); + } + else { + rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, nullptr); + } + + return journalDirectory.forget(); +} + +already_AddRefed +FileManager::GetFileInfo(int64_t aId) +{ + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + return nullptr; + } + + FileInfo* fileInfo = nullptr; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + fileInfo = mFileInfos.Get(aId); + } + nsRefPtr result = fileInfo; + return result.forget(); +} + +already_AddRefed +FileManager::GetNewFileInfo() +{ + if (IndexedDatabaseManager::IsClosed()) { + NS_ERROR("Shouldn't be called after shutdown!"); + return nullptr; + } + + nsAutoPtr fileInfo; + + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + + int64_t id = mLastFileId + 1; + + fileInfo = FileInfo::Create(this, id); + + mFileInfos.Put(id, fileInfo); + + mLastFileId = id; + } + + nsRefPtr result = fileInfo.forget(); + return result.forget(); +} + +// static +already_AddRefed +FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId) +{ + NS_ASSERTION(aDirectory, "Null pointer!"); + + nsAutoString id; + id.AppendInt(aId); + + nsCOMPtr file; + nsresult rv = aDirectory->Clone(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = file->Append(id); + NS_ENSURE_SUCCESS(rv, nullptr); + + return file.forget(); +} + +// static +nsresult +FileManager::InitDirectory(nsIFile* aDirectory, + nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin) +{ + AssertIsOnIOThread(); + NS_ASSERTION(aDirectory, "Null directory!"); + NS_ASSERTION(aDatabaseFile, "Null database file!"); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) { + return NS_OK; + } + + bool isDirectory; + rv = aDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + + nsCOMPtr journalDirectory; + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + rv = journalDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); + + nsCOMPtr entries; + rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasElements; + rv = entries->HasMoreElements(&hasElements); + NS_ENSURE_SUCCESS(rv, rv); + + if (hasElements) { + nsCOMPtr connection; + rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile, + aDirectory, NullString(), aPersistenceType, aGroup, aOrigin, + getter_AddRefs(connection)); + NS_ENSURE_SUCCESS(rv, rv); + + mozStorageTransaction transaction(connection, false); + + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE VIRTUAL TABLE fs USING filesystem;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr stmt; + rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, (name IN (SELECT id FROM file)) FROM fs " + "WHERE path = :path" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString path; + rv = journalDirectory->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { + nsString name; + rv = stmt->GetString(0, name); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t flag = stmt->AsInt32(1); + + if (!flag) { + nsCOMPtr file; + rv = aDirectory->Clone(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = file->Append(name); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(file->Remove(false))) { + NS_WARNING("Failed to remove orphaned file!"); + } + } + + nsCOMPtr journalFile; + rv = journalDirectory->Clone(getter_AddRefs(journalFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = journalFile->Append(name); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_FAILED(journalFile->Remove(false))) { + NS_WARNING("Failed to remove journal file!"); + } + } + + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE fs;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + transaction.Commit(); + } + } + + return NS_OK; +} + +// static +nsresult +FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) +{ + AssertIsOnIOThread(); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) { + *aUsage = 0; + return NS_OK; + } + + nsCOMPtr entries; + rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t usage = 0; + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr file = do_QueryInterface(entry); + NS_ENSURE_TRUE(file, NS_NOINTERFACE); + + nsString leafName; + rv = file->GetLeafName(leafName); + NS_ENSURE_SUCCESS(rv, rv); + + if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { + continue; + } + + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + quota::IncrementUsage(&usage, uint64_t(fileSize)); + } + + *aUsage = usage; + return NS_OK; +} diff --git a/dom/indexedDB/FileManager.h b/dom/indexedDB/FileManager.h index a50546dd1549..83734a65320b 100644 --- a/dom/indexedDB/FileManager.h +++ b/dom/indexedDB/FileManager.h @@ -7,23 +7,21 @@ #ifndef mozilla_dom_indexeddb_filemanager_h__ #define mozilla_dom_indexeddb_filemanager_h__ -#include "mozilla/Attributes.h" +#include "IndexedDatabase.h" + +#include "nsIDOMFile.h" +#include "nsIFile.h" + #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/dom/quota/StoragePrivilege.h" #include "nsDataHashtable.h" -#include "nsHashKeys.h" -#include "nsISupportsImpl.h" -class nsIFile; class mozIStorageConnection; -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE class FileInfo; -// Implemented in ActorsParent.cpp. class FileManager MOZ_FINAL { friend class FileInfo; @@ -31,6 +29,79 @@ class FileManager MOZ_FINAL typedef mozilla::dom::quota::PersistenceType PersistenceType; typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege; +public: + FileManager(PersistenceType aPersistenceType, const nsACString& aGroup, + const nsACString& aOrigin, StoragePrivilege aPrivilege, + const nsAString& aDatabaseName) + : mPersistenceType(aPersistenceType), mGroup(aGroup), mOrigin(aOrigin), + mPrivilege(aPrivilege), mDatabaseName(aDatabaseName), mLastFileId(0), + mInvalidated(false) + { } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager) + + PersistenceType Type() + { + return mPersistenceType; + } + + const nsACString& Group() const + { + return mGroup; + } + + const nsACString& Origin() const + { + return mOrigin; + } + + const StoragePrivilege& Privilege() const + { + return mPrivilege; + } + + const nsAString& DatabaseName() const + { + return mDatabaseName; + } + + bool Invalidated() const + { + return mInvalidated; + } + + nsresult Init(nsIFile* aDirectory, + mozIStorageConnection* aConnection); + + nsresult Invalidate(); + + already_AddRefed GetDirectory(); + + already_AddRefed GetJournalDirectory(); + + already_AddRefed EnsureJournalDirectory(); + + already_AddRefed GetFileInfo(int64_t aId); + + already_AddRefed GetNewFileInfo(); + + static already_AddRefed GetFileForId(nsIFile* aDirectory, + int64_t aId); + + static nsresult InitDirectory(nsIFile* aDirectory, + nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin); + + static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage); + +private: + // Private destructor, to discourage deletion outside of Release(): + ~FileManager() + { + } + PersistenceType mPersistenceType; nsCString mGroup; nsCString mOrigin; @@ -46,92 +117,8 @@ class FileManager MOZ_FINAL nsDataHashtable mFileInfos; bool mInvalidated; - -public: - static already_AddRefed - GetFileForId(nsIFile* aDirectory, int64_t aId); - - static nsresult - InitDirectory(nsIFile* aDirectory, - nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin); - - static nsresult - GetUsage(nsIFile* aDirectory, uint64_t* aUsage); - - FileManager(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - StoragePrivilege aPrivilege, - const nsAString& aDatabaseName); - - PersistenceType - Type() const - { - return mPersistenceType; - } - - const nsACString& - Group() const - { - return mGroup; - } - - const nsACString& - Origin() const - { - return mOrigin; - } - - const StoragePrivilege& - Privilege() const - { - return mPrivilege; - } - - const nsAString& - DatabaseName() const - { - return mDatabaseName; - } - - bool - Invalidated() const - { - return mInvalidated; - } - - nsresult - Init(nsIFile* aDirectory, mozIStorageConnection* aConnection); - - nsresult - Invalidate(); - - already_AddRefed - GetDirectory(); - - already_AddRefed - GetJournalDirectory(); - - already_AddRefed - EnsureJournalDirectory(); - - already_AddRefed - GetFileInfo(int64_t aId); - - already_AddRefed - GetNewFileInfo(); - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager) - -private: - ~FileManager(); }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_filemanager_h__ diff --git a/dom/indexedDB/FileSnapshot.cpp b/dom/indexedDB/FileSnapshot.cpp index c1048c67c1fd..32f04c55f16f 100644 --- a/dom/indexedDB/FileSnapshot.cpp +++ b/dom/indexedDB/FileSnapshot.cpp @@ -7,56 +7,39 @@ #include "FileSnapshot.h" #include "IDBFileHandle.h" -#include "MainThreadUtils.h" #include "mozilla/Assertions.h" -#include "mozilla/dom/MetadataHelper.h" - -#ifdef DEBUG -#include "nsXULAppAPI.h" -#endif +#include "nsDebug.h" namespace mozilla { namespace dom { namespace indexedDB { +NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, DOMFileImpl) + // Create as a stored file FileImplSnapshot::FileImplSnapshot(const nsAString& aName, const nsAString& aContentType, - MetadataParameters* aMetadataParams, - nsIFile* aFile, + uint64_t aLength, nsIFile* aFile, IDBFileHandle* aFileHandle, FileInfo* aFileInfo) - : DOMFileImplBase(aName, - aContentType, - aMetadataParams->Size(), - aMetadataParams->LastModified()) - , mFile(aFile) - , mFileHandle(aFileHandle) - , mWholeFile(true) + : DOMFileImplBase(aName, aContentType, aLength), + mFile(aFile), mFileHandle(aFileHandle), mWholeFile(true) { - AssertSanity(); - MOZ_ASSERT(aMetadataParams); - MOZ_ASSERT(aMetadataParams->Size() != UINT64_MAX); - MOZ_ASSERT(aMetadataParams->LastModified() != INT64_MAX); - MOZ_ASSERT(aFile); - MOZ_ASSERT(aFileHandle); - MOZ_ASSERT(aFileInfo); - + MOZ_ASSERT(mFile, "Null file!"); + MOZ_ASSERT(mFileHandle, "Null file handle!"); mFileInfos.AppendElement(aFileInfo); } // Create slice FileImplSnapshot::FileImplSnapshot(const FileImplSnapshot* aOther, - uint64_t aStart, - uint64_t aLength, + uint64_t aStart, uint64_t aLength, const nsAString& aContentType) - : DOMFileImplBase(aContentType, aOther->mStart + aStart, aLength) - , mFile(aOther->mFile) - , mFileHandle(aOther->mFileHandle) - , mWholeFile(false) + : DOMFileImplBase(aContentType, aOther->mStart + aStart, aLength), + mFile(aOther->mFile), mFileHandle(aOther->mFileHandle), + mWholeFile(false) { - AssertSanity(); - MOZ_ASSERT(aOther); + MOZ_ASSERT(mFile, "Null file!"); + MOZ_ASSERT(mFileHandle, "Null file handle!"); FileInfo* fileInfo; @@ -74,25 +57,9 @@ FileImplSnapshot::~FileImplSnapshot() { } -#ifdef DEBUG - -// static -void -FileImplSnapshot::AssertSanity() -{ - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - MOZ_ASSERT(NS_IsMainThread()); -} - -#endif // DEBUG - -NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, DOMFileImpl) - void FileImplSnapshot::Unlink() { - AssertSanity(); - FileImplSnapshot* tmp = this; NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileHandle); } @@ -100,80 +67,43 @@ FileImplSnapshot::Unlink() void FileImplSnapshot::Traverse(nsCycleCollectionTraversalCallback &cb) { - AssertSanity(); - FileImplSnapshot* tmp = this; NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileHandle); } -bool -FileImplSnapshot::IsCCed() const -{ - AssertSanity(); - - return true; -} - nsresult FileImplSnapshot::GetInternalStream(nsIInputStream** aStream) { - AssertSanity(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsresult rv = mFileHandle->OpenInputStream(mWholeFile, mStart, mLength, aStream); - if (NS_FAILED(rv)) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } -already_AddRefed -FileImplSnapshot::CreateSlice(uint64_t aStart, - uint64_t aLength, +already_AddRefed +FileImplSnapshot::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - AssertSanity(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsRefPtr impl = - new FileImplSnapshot(this, aStart, aLength, aContentType); + nsCOMPtr t = + new DOMFile(new FileImplSnapshot(this, aStart, aLength, aContentType)); - return impl.forget(); + return t.forget(); } nsresult FileImplSnapshot::GetMozFullPathInternal(nsAString& aFilename) { - AssertSanity(); - MOZ_ASSERT(mIsFile); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mIsFile, "Should only be called on files"); return mFile->GetPath(aFilename); } -bool -FileImplSnapshot::IsStoredFile() const -{ - AssertSanity(); - - return true; -} - -bool -FileImplSnapshot::IsWholeFile() const -{ - AssertSanity(); - - return mWholeFile; -} - -bool -FileImplSnapshot::IsSnapshot() const -{ - AssertSanity(); - - return true; -} - } // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/FileSnapshot.h b/dom/indexedDB/FileSnapshot.h index 5816d3ee15b1..c3aa2749dfe7 100644 --- a/dom/indexedDB/FileSnapshot.h +++ b/dom/indexedDB/FileSnapshot.h @@ -10,55 +10,26 @@ #include "mozilla/Attributes.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" #include "nsDOMFile.h" namespace mozilla { namespace dom { - -class MetadataParameters; - namespace indexedDB { class IDBFileHandle; -class FileImplSnapshot MOZ_FINAL - : public DOMFileImplBase +class FileImplSnapshot : public DOMFileImplBase { - typedef mozilla::dom::MetadataParameters MetadataParameters; - - nsCOMPtr mFile; - nsRefPtr mFileHandle; - - bool mWholeFile; - public: - // Create as a stored file - FileImplSnapshot(const nsAString& aName, - const nsAString& aContentType, - MetadataParameters* aMetadataParams, - nsIFile* aFile, - IDBFileHandle* aFileHandle, - FileInfo* aFileInfo); - NS_DECL_ISUPPORTS_INHERITED -private: - // Create slice - FileImplSnapshot(const FileImplSnapshot* aOther, - uint64_t aStart, - uint64_t aLength, - const nsAString& aContentType); - - ~FileImplSnapshot(); - - static void - AssertSanity() -#ifdef DEBUG - ; -#else - { } -#endif + // Create as a stored file + FileImplSnapshot(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, nsIFile* aFile, IDBFileHandle* aFileHandle, + FileInfo* aFileInfo); + // Overrides virtual nsresult GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE; @@ -72,21 +43,45 @@ private: Traverse(nsCycleCollectionTraversalCallback &aCb) MOZ_OVERRIDE; virtual bool - IsCCed() const MOZ_OVERRIDE; + IsCCed() const MOZ_OVERRIDE + { + return true; + } - virtual already_AddRefed - CreateSlice(uint64_t aStart, - uint64_t aLength, +protected: + // Create slice + FileImplSnapshot(const FileImplSnapshot* aOther, uint64_t aStart, + uint64_t aLength, const nsAString& aContentType); + + virtual ~FileImplSnapshot(); + + virtual already_AddRefed + CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; virtual bool - IsStoredFile() const MOZ_OVERRIDE; + IsStoredFile() const MOZ_OVERRIDE + { + return true; + } virtual bool - IsWholeFile() const MOZ_OVERRIDE; + IsWholeFile() const MOZ_OVERRIDE + { + return mWholeFile; + } virtual bool - IsSnapshot() const MOZ_OVERRIDE; + IsSnapshot() const MOZ_OVERRIDE + { + return true; + } + +private: + nsCOMPtr mFile; + nsRefPtr mFileHandle; + + bool mWholeFile; }; } // namespace indexedDB diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index af9594773d10..ec985566ff1b 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -4,194 +4,338 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "base/basictypes.h" + #include "IDBCursor.h" -#include "IDBDatabase.h" +#include "mozilla/storage.h" +#include "nsComponentManagerUtils.h" +#include "nsJSUtils.h" +#include "nsThreadUtils.h" + +#include "AsyncConnectionHelper.h" +#include "IDBEvents.h" #include "IDBIndex.h" #include "IDBObjectStore.h" -#include "IDBRequest.h" #include "IDBTransaction.h" -#include "IndexedDatabaseInlines.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/UnionTypes.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" -#include "nsString.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" +#include "TransactionThreadPool.h" -// Include this last to avoid path problems on Windows. -#include "ActorsChild.h" +#include "ipc/IndexedDBChild.h" +#include "ipc/IndexedDBParent.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "IndexedDatabaseInlines.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/UnionTypes.h" -IDBCursor::IDBCursor(Type aType, - IDBObjectStore* aSourceObjectStore, - IDBIndex* aSourceIndex, - IDBTransaction* aTransaction, - BackgroundCursorChild* aBackgroundActor, - Direction aDirection, - const Key& aKey) - : mSourceObjectStore(aSourceObjectStore) - , mSourceIndex(aSourceIndex) - , mTransaction(aTransaction) - , mBackgroundActor(aBackgroundActor) - , mScriptOwner(aTransaction->Database()->GetScriptOwner()) - , mCachedKey(JSVAL_VOID) - , mCachedPrimaryKey(JSVAL_VOID) - , mCachedValue(JSVAL_VOID) - , mKey(aKey) - , mType(aType) - , mDirection(aDirection) - , mHaveCachedKey(false) - , mHaveCachedPrimaryKey(false) - , mHaveCachedValue(false) - , mRooted(false) - , mContinueCalled(false) - , mHaveValue(true) +USING_INDEXEDDB_NAMESPACE +using namespace mozilla::dom::indexedDB::ipc; +using mozilla::dom::Optional; +using mozilla::dom::OwningIDBObjectStoreOrIDBIndex; +using mozilla::ErrorResult; + +static_assert(sizeof(size_t) >= sizeof(IDBCursor::Direction), + "Relying on conversion between size_t and IDBCursor::Direction"); + +namespace { + +class CursorHelper : public AsyncConnectionHelper { - MOZ_ASSERT_IF(aType == Type_ObjectStore || aType == Type_ObjectStoreKey, - aSourceObjectStore); - MOZ_ASSERT_IF(aType == Type_Index || aType == Type_IndexKey, aSourceIndex); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!aKey.IsUnset()); - MOZ_ASSERT(mScriptOwner); - - SetIsDOMBinding(); - - if (mScriptOwner) { - mozilla::HoldJSObjects(this); - mRooted = true; +public: + explicit CursorHelper(IDBCursor* aCursor) + : AsyncConnectionHelper(aCursor->Transaction(), aCursor->Request()), + mCursor(aCursor), mActor(nullptr) + { + NS_ASSERTION(aCursor, "Null cursor!"); } -} -IDBCursor::~IDBCursor() + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(CursorRequestParams& aParams) = 0; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; + +protected: + virtual ~CursorHelper() + { } + + nsRefPtr mCursor; + +private: + IndexedDBCursorRequestChild* mActor; +}; + +} // anonymous namespace + +BEGIN_INDEXEDDB_NAMESPACE + +class ContinueHelper : public CursorHelper { - AssertIsOnOwningThread(); - - DropJSObjects(); - - if (mBackgroundActor) { - mBackgroundActor->SendDeleteMeInternal(); - MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); +public: + ContinueHelper(IDBCursor* aCursor, + int32_t aCount) + : CursorHelper(aCursor), mCount(aCount) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aCursor); + MOZ_ASSERT(aCount > 0); } -} + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) + MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(CursorRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + virtual ~ContinueHelper() + { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + } + + virtual nsresult + BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0; + + virtual nsresult + GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0; + + void UpdateCursorState() + { + mCursor->mCachedKey = JSVAL_VOID; + mCursor->mCachedPrimaryKey = JSVAL_VOID; + mCursor->mCachedValue = JSVAL_VOID; + mCursor->mHaveCachedKey = false; + mCursor->mHaveCachedPrimaryKey = false; + mCursor->mHaveCachedValue = false; + mCursor->mContinueCalled = false; + + if (mKey.IsUnset()) { + mCursor->mHaveValue = false; + } else { + MOZ_ASSERT(mCursor->mType == IDBCursor::OBJECTSTORE || + mCursor->mType == IDBCursor::OBJECTSTOREKEY || + !mObjectKey.IsUnset()); + + // Set new values. + mCursor->mKey = mKey; + mCursor->mObjectKey = mObjectKey; + mCursor->mContinueToKey.Unset(); + + mCursor->mCloneReadInfo = Move(mCloneReadInfo); + mCloneReadInfo.mCloneBuffer.clear(); + } + } + + int32_t mCount; + Key mKey; + Key mObjectKey; + StructuredCloneReadInfo mCloneReadInfo; +}; + +class ContinueObjectStoreHelper : public ContinueHelper +{ +public: + ContinueObjectStoreHelper(IDBCursor* aCursor, + uint32_t aCount) + : ContinueHelper(aCursor, aCount) + { } + +protected: + virtual ~ContinueObjectStoreHelper() + { } + +private: + nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement); + nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); +}; + +class ContinueObjectStoreKeyHelper : public ContinueObjectStoreHelper +{ +public: + ContinueObjectStoreKeyHelper(IDBCursor* aCursor, + uint32_t aCount) + : ContinueObjectStoreHelper(aCursor, aCount) + { } + +private: + virtual ~ContinueObjectStoreKeyHelper() + { } + + virtual nsresult + GatherResultsFromStatement(mozIStorageStatement* aStatement) MOZ_OVERRIDE; +}; + +class ContinueIndexHelper : public ContinueHelper +{ +public: + ContinueIndexHelper(IDBCursor* aCursor, + uint32_t aCount) + : ContinueHelper(aCursor, aCount) + { } + +protected: + virtual ~ContinueIndexHelper() + { } + +private: + nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement); + nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); +}; + +class ContinueIndexObjectHelper : public ContinueIndexHelper +{ +public: + ContinueIndexObjectHelper(IDBCursor* aCursor, + uint32_t aCount) + : ContinueIndexHelper(aCursor, aCount) + { } + +private: + virtual ~ContinueIndexObjectHelper() + { } + + nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); +}; + +END_INDEXEDDB_NAMESPACE // static already_AddRefed -IDBCursor::Create(IDBObjectStore* aObjectStore, - BackgroundCursorChild* aBackgroundActor, +IDBCursor::Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey, - StructuredCloneReadInfo&& aCloneInfo) + StructuredCloneReadInfo&& aCloneReadInfo) { - MOZ_ASSERT(aObjectStore); - aObjectStore->AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!aKey.IsUnset()); + NS_ASSERTION(aObjectStore, "Null pointer!"); + NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); nsRefPtr cursor = - new IDBCursor(Type_ObjectStore, - aObjectStore, - nullptr, - aObjectStore->Transaction(), - aBackgroundActor, - aDirection, - aKey); + IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, + aRangeKey, aContinueQuery, aContinueToQuery); + NS_ASSERTION(cursor, "This shouldn't fail!"); - cursor->mCloneInfo = Move(aCloneInfo); + cursor->mObjectStore = aObjectStore; + cursor->mType = OBJECTSTORE; + cursor->mKey = aKey; + cursor->mCloneReadInfo = Move(aCloneReadInfo); return cursor.forget(); } // static already_AddRefed -IDBCursor::Create(IDBObjectStore* aObjectStore, - BackgroundCursorChild* aBackgroundActor, +IDBCursor::Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey) { MOZ_ASSERT(aObjectStore); - aObjectStore->AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); MOZ_ASSERT(!aKey.IsUnset()); nsRefPtr cursor = - new IDBCursor(Type_ObjectStoreKey, - aObjectStore, - nullptr, - aObjectStore->Transaction(), - aBackgroundActor, - aDirection, - aKey); + IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, + aRangeKey, aContinueQuery, aContinueToQuery); + NS_ASSERTION(cursor, "This shouldn't fail!"); + + cursor->mObjectStore = aObjectStore; + cursor->mType = OBJECTSTOREKEY; + cursor->mKey = aKey; return cursor.forget(); } // static already_AddRefed -IDBCursor::Create(IDBIndex* aIndex, - BackgroundCursorChild* aBackgroundActor, +IDBCursor::Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBIndex* aIndex, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey, - const Key& aPrimaryKey, - StructuredCloneReadInfo&& aCloneInfo) + const Key& aObjectKey) { - MOZ_ASSERT(aIndex); - aIndex->AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!aKey.IsUnset()); - MOZ_ASSERT(!aPrimaryKey.IsUnset()); + NS_ASSERTION(aIndex, "Null pointer!"); + NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); + NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!"); nsRefPtr cursor = - new IDBCursor(Type_Index, - nullptr, - aIndex, - aIndex->ObjectStore()->Transaction(), - aBackgroundActor, - aDirection, - aKey); + IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), + aDirection, aRangeKey, aContinueQuery, + aContinueToQuery); + NS_ASSERTION(cursor, "This shouldn't fail!"); - cursor->mPrimaryKey = Move(aPrimaryKey); - cursor->mCloneInfo = Move(aCloneInfo); + cursor->mIndex = aIndex; + cursor->mType = INDEXKEY; + cursor->mKey = aKey, + cursor->mObjectKey = aObjectKey; return cursor.forget(); } // static already_AddRefed -IDBCursor::Create(IDBIndex* aIndex, - BackgroundCursorChild* aBackgroundActor, +IDBCursor::Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBIndex* aIndex, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey, - const Key& aPrimaryKey) + const Key& aObjectKey, + StructuredCloneReadInfo&& aCloneReadInfo) { - MOZ_ASSERT(aIndex); - aIndex->AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!aKey.IsUnset()); - MOZ_ASSERT(!aPrimaryKey.IsUnset()); + NS_ASSERTION(aIndex, "Null pointer!"); + NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); nsRefPtr cursor = - new IDBCursor(Type_IndexKey, - nullptr, - aIndex, - aIndex->ObjectStore()->Transaction(), - aBackgroundActor, - aDirection, - aKey); + IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), + aDirection, aRangeKey, aContinueQuery, + aContinueToQuery); + NS_ASSERTION(cursor, "This shouldn't fail!"); - cursor->mPrimaryKey = Move(aPrimaryKey); + cursor->mObjectStore = aIndex->ObjectStore(); + cursor->mIndex = aIndex; + cursor->mType = INDEXOBJECT; + cursor->mKey = aKey; + cursor->mObjectKey = aObjectKey; + cursor->mCloneReadInfo = Move(aCloneReadInfo); return cursor.forget(); } // static -auto -IDBCursor::ConvertDirection(IDBCursorDirection aDirection) -> Direction +IDBCursor::Direction +IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection) { switch (aDirection) { case mozilla::dom::IDBCursorDirection::Next: @@ -211,77 +355,232 @@ IDBCursor::ConvertDirection(IDBCursorDirection aDirection) -> Direction } } -#ifdef DEBUG - -void -IDBCursor::AssertIsOnOwningThread() const +// static +already_AddRefed +IDBCursor::CreateCommon(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, + Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery) { - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aRequest, "Null pointer!"); + NS_ASSERTION(aTransaction, "Null pointer!"); + NS_ASSERTION(aObjectStore, "Null pointer!"); + NS_ASSERTION(!aContinueQuery.IsEmpty() || + !IndexedDatabaseManager::IsMainProcess(), + "Empty query!"); + NS_ASSERTION(!aContinueToQuery.IsEmpty() || + !IndexedDatabaseManager::IsMainProcess(), + "Empty query!"); + + nsRefPtr cursor = new IDBCursor(); + + IDBDatabase* database = aTransaction->Database(); + cursor->mScriptOwner = database->GetScriptOwner(); + + if (cursor->mScriptOwner) { + mozilla::HoldJSObjects(cursor.get()); + cursor->mRooted = true; + } + + cursor->mRequest = aRequest; + cursor->mTransaction = aTransaction; + cursor->mObjectStore = aObjectStore; + cursor->mDirection = aDirection; + cursor->mContinueQuery = aContinueQuery; + cursor->mContinueToQuery = aContinueToQuery; + cursor->mRangeKey = aRangeKey; + + return cursor.forget(); } -#endif // DEBUG +IDBCursor::IDBCursor() +: mScriptOwner(nullptr), + mType(OBJECTSTORE), + mDirection(IDBCursor::NEXT), + mCachedKey(JSVAL_VOID), + mCachedPrimaryKey(JSVAL_VOID), + mCachedValue(JSVAL_VOID), + mActorChild(nullptr), + mActorParent(nullptr), + mHaveCachedKey(false), + mHaveCachedPrimaryKey(false), + mHaveCachedValue(false), + mRooted(false), + mContinueCalled(false), + mHaveValue(true) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + SetIsDOMBinding(); +} + +IDBCursor::~IDBCursor() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->Send__delete__(mActorChild); + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); + } + + DropJSObjects(); + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); +} void IDBCursor::DropJSObjects() { - AssertIsOnOwningThread(); - - Reset(); - if (!mRooted) { return; } - mScriptOwner = nullptr; + mCachedKey = JSVAL_VOID; + mCachedPrimaryKey = JSVAL_VOID; + mCachedValue = JSVAL_VOID; + mHaveCachedKey = false; + mHaveCachedPrimaryKey = false; + mHaveCachedValue = false; mRooted = false; - + mHaveValue = false; mozilla::DropJSObjects(this); } void -IDBCursor::Reset() +IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aCount > 0); - mCachedKey.setUndefined(); - mCachedPrimaryKey.setUndefined(); - mCachedValue.setUndefined(); - IDBObjectStore::ClearCloneReadInfo(mCloneInfo); + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return; + } - mHaveCachedKey = false; - mHaveCachedPrimaryKey = false; - mHaveCachedValue = false; - mHaveValue = false; - mContinueCalled = false; + if (!mHaveValue || mContinueCalled) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return; + } + + mContinueToKey = aKey; + + MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done); + + mRequest->Reset(); + + nsRefPtr helper; + switch (mType) { + case OBJECTSTORE: + helper = new ContinueObjectStoreHelper(this, aCount); + break; + + case OBJECTSTOREKEY: + helper = new ContinueObjectStoreKeyHelper(this, aCount); + break; + + case INDEXKEY: + helper = new ContinueIndexHelper(this, aCount); + break; + + case INDEXOBJECT: + helper = new ContinueIndexObjectHelper(this, aCount); + break; + + default: + MOZ_CRASH("Unknown cursor type!"); + } + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return; + } + + mContinueCalled = true; } -nsPIDOMWindow* -IDBCursor::GetParentObject() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mTransaction); +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor) - return mTransaction->GetParentObject(); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndex) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_ASSERTION(tmp->mHaveCachedKey || tmp->mCachedKey.isUndefined(), + "Should have a cached key"); + NS_ASSERTION(tmp->mHaveCachedPrimaryKey || + tmp->mCachedPrimaryKey.isUndefined(), + "Should have a cached primary key"); + NS_ASSERTION(tmp->mHaveCachedValue || tmp->mCachedValue.isUndefined(), + "Should have a cached value"); + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + // Don't unlink mObjectStore, mIndex, or mTransaction! + tmp->DropJSObjects(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor) + +JSObject* +IDBCursor::WrapObject(JSContext* aCx) +{ + MOZ_ASSERT(NS_IsMainThread()); + + switch (mType) { + case OBJECTSTORE: + case INDEXOBJECT: + return IDBCursorWithValueBinding::Wrap(aCx, this); + + case OBJECTSTOREKEY: + case INDEXKEY: + return IDBCursorBinding::Wrap(aCx, this); + + default: + MOZ_CRASH("Bad type!"); + } } -IDBCursorDirection +mozilla::dom::IDBCursorDirection IDBCursor::GetDirection() const { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); switch (mDirection) { case NEXT: - return IDBCursorDirection::Next; + return mozilla::dom::IDBCursorDirection::Next; case NEXT_UNIQUE: - return IDBCursorDirection::Nextunique; + return mozilla::dom::IDBCursorDirection::Nextunique; case PREV: - return IDBCursorDirection::Prev; + return mozilla::dom::IDBCursorDirection::Prev; case PREV_UNIQUE: - return IDBCursorDirection::Prevunique; + return mozilla::dom::IDBCursorDirection::Prevunique; default: MOZ_CRASH("Bad direction!"); @@ -291,20 +590,20 @@ IDBCursor::GetDirection() const void IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); switch (mType) { - case Type_ObjectStore: - case Type_ObjectStoreKey: - MOZ_ASSERT(mSourceObjectStore); - aSource.SetAsIDBObjectStore() = mSourceObjectStore; - return; + case OBJECTSTORE: + case OBJECTSTOREKEY: + MOZ_ASSERT(mObjectStore); + aSource.SetAsIDBObjectStore() = mObjectStore; + break; - case Type_Index: - case Type_IndexKey: - MOZ_ASSERT(mSourceIndex); - aSource.SetAsIDBIndex() = mSourceIndex; - return; + case INDEXKEY: + case INDEXOBJECT: + MOZ_ASSERT(mIndex); + aSource.SetAsIDBIndex() = mIndex; + break; default: MOZ_ASSERT_UNREACHABLE("Bad type!"); @@ -315,8 +614,7 @@ void IDBCursor::GetKey(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - AssertIsOnOwningThread(); - + MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue); if (!mHaveValue) { @@ -346,7 +644,7 @@ void IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); if (!mHaveValue) { aResult.setUndefined(); @@ -360,10 +658,7 @@ IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, } const Key& key = - (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) ? - mKey : - mPrimaryKey; - + (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey; MOZ_ASSERT(!key.IsUnset()); aRv = key.ToJSVal(aCx, mCachedPrimaryKey); @@ -382,8 +677,8 @@ void IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - AssertIsOnOwningThread(); - MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); if (!mHaveValue) { aResult.setUndefined(); @@ -397,12 +692,12 @@ IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle aResult, } JS::Rooted val(aCx); - if (NS_WARN_IF(!IDBObjectStore::DeserializeValue(aCx, mCloneInfo, &val))) { + if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &val)) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } - IDBObjectStore::ClearCloneReadInfo(mCloneInfo); + mCloneReadInfo.mCloneBuffer.clear(); mCachedValue = val; mHaveCachedValue = true; @@ -417,36 +712,24 @@ IDBCursor::Continue(JSContext* aCx, JS::Handle aKey, ErrorResult &aRv) { - AssertIsOnOwningThread(); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return; - } - - if (!mHaveValue || mContinueCalled) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return; - } + MOZ_ASSERT(NS_IsMainThread()); Key key; aRv = key.SetFromJSVal(aCx, aKey); - if (aRv.Failed()) { - return; - } + ENSURE_SUCCESS_VOID(aRv); if (!key.IsUnset()) { switch (mDirection) { - case NEXT: - case NEXT_UNIQUE: + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: if (key <= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; - case PREV: - case PREV_UNIQUE: + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: if (key >= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; @@ -458,12 +741,13 @@ IDBCursor::Continue(JSContext* aCx, } } - mBackgroundActor->SendContinueInternal(ContinueParams(key)); - - mContinueCalled = true; + ContinueInternal(key, 1, aRv); + if (aRv.Failed()) { + return; + } #ifdef IDB_PROFILER_USE_MARKS - if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) { + if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).cursor(%s)." "continue(%s)", @@ -471,10 +755,11 @@ IDBCursor::Continue(JSContext* aCx, Request()->GetSerialNumber(), IDB_PROFILER_STRING(Transaction()->Database()), IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mSourceObjectStore), + IDB_PROFILER_STRING(mObjectStore), IDB_PROFILER_STRING(mDirection), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); - } else { + } + else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "cursor(%s).continue(%s)", @@ -482,107 +767,46 @@ IDBCursor::Continue(JSContext* aCx, Request()->GetSerialNumber(), IDB_PROFILER_STRING(Transaction()->Database()), IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mSourceIndex->ObjectStore()), - IDB_PROFILER_STRING(mSourceIndex), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(mIndex), IDB_PROFILER_STRING(mDirection), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); } #endif } -void -IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv) -{ - AssertIsOnOwningThread(); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return; - } - - if (!mHaveValue || mContinueCalled) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return; - } - - if (!aCount) { - aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT); - return; - } - - mBackgroundActor->SendContinueInternal(AdvanceParams(aCount)); - - mContinueCalled = true; - -#ifdef IDB_PROFILER_USE_MARKS - { - if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "cursor(%s).advance(%ld)", - "IDBRequest[%llu] MT IDBCursor.advance()", - Request()->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mSourceObjectStore), - IDB_PROFILER_STRING(mDirection), aCount); - } else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "index(%s).cursor(%s).advance(%ld)", - "IDBRequest[%llu] MT IDBCursor.advance()", - Request()->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mSourceIndex->ObjectStore()), - IDB_PROFILER_STRING(mSourceIndex), - IDB_PROFILER_STRING(mDirection), aCount); - } - } -#endif -} - already_AddRefed IDBCursor::Update(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return nullptr; - } - if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); - MOZ_ASSERT(!mKey.IsUnset()); - MOZ_ASSERT_IF(mType == Type_Index, !mPrimaryKey.IsUnset()); - - IDBObjectStore* objectStore; - if (mType == Type_ObjectStore) { - objectStore = mSourceObjectStore; - } else { - objectStore = mSourceIndex->ObjectStore(); + if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; } - MOZ_ASSERT(objectStore); + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(!mKey.IsUnset()); + MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); + MOZ_ASSERT_IF(mType == INDEXOBJECT, !mObjectKey.IsUnset()); - const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; + const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; nsRefPtr request; - - if (objectStore->HasValidKeyPath()) { + if (mObjectStore->HasValidKeyPath()) { // Make sure the object given has the correct keyPath value set on it. - const KeyPath& keyPath = objectStore->GetKeyPath(); + const KeyPath& keyPath = mObjectStore->GetKeyPath(); Key key; aRv = keyPath.ExtractKey(aCx, aValue, key); @@ -590,35 +814,32 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, return nullptr; } - if (key != primaryKey) { + if (key != objectKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return nullptr; } - request = objectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv); + request = mObjectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv); if (aRv.Failed()) { return nullptr; } } else { JS::Rooted keyVal(aCx); - aRv = primaryKey.ToJSVal(aCx, &keyVal); - if (aRv.Failed()) { - return nullptr; - } + aRv = objectKey.ToJSVal(aCx, &keyVal); + ENSURE_SUCCESS(aRv, nullptr); - request = objectStore->Put(aCx, aValue, keyVal, aRv); + request = mObjectStore->Put(aCx, aValue, keyVal, aRv); if (aRv.Failed()) { return nullptr; } } - request->SetSource(this); - #ifdef IDB_PROFILER_USE_MARKS { - uint64_t requestSerial = request->GetSerialNumber(); - if (mType == Type_ObjectStore) { + uint64_t requestSerial = + static_cast(request.get())->GetSerialNumber(); + if (mType == OBJECTSTORE) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).update(%s)", @@ -626,11 +847,12 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(objectStore), + IDB_PROFILER_STRING(mObjectStore), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(primaryKey)); - } else { + IDB_PROFILER_STRING(objectKey)); + } + else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).update(%s)", @@ -638,11 +860,11 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(objectStore), - IDB_PROFILER_STRING(mSourceIndex), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(mIndex), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(primaryKey)); + IDB_PROFILER_STRING(objectKey)); } } #endif @@ -653,54 +875,40 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, already_AddRefed IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return nullptr; - } - if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); + if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); MOZ_ASSERT(!mKey.IsUnset()); - IDBObjectStore* objectStore; - if (mType == Type_ObjectStore) { - objectStore = mSourceObjectStore; - } else { - objectStore = mSourceIndex->ObjectStore(); - } - - MOZ_ASSERT(objectStore); - - const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; + const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; JS::Rooted key(aCx); - aRv = primaryKey.ToJSVal(aCx, &key); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } + aRv = objectKey.ToJSVal(aCx, &key); + ENSURE_SUCCESS(aRv, nullptr); - nsRefPtr request = objectStore->Delete(aCx, key, aRv); - if (aRv.Failed()) { - return nullptr; - } - - request->SetSource(this); + nsRefPtr request = mObjectStore->Delete(aCx, key, aRv); + ENSURE_SUCCESS(aRv, nullptr); #ifdef IDB_PROFILER_USE_MARKS { uint64_t requestSerial = request->GetSerialNumber(); - if (mType == Type_ObjectStore) { + if (mType == OBJECTSTORE) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).delete(%s)", @@ -708,11 +916,12 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(objectStore), + IDB_PROFILER_STRING(mObjectStore), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(primaryKey)); - } else { + IDB_PROFILER_STRING(objectKey)); + } + else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).delete(%s)", @@ -720,11 +929,11 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(objectStore), - IDB_PROFILER_STRING(mSourceIndex), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(mIndex), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(primaryKey)); + IDB_PROFILER_STRING(objectKey)); } } #endif @@ -733,118 +942,408 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) } void -IDBCursor::Reset(Key&& aKey, StructuredCloneReadInfo&& aValue) +IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv) { - AssertIsOnOwningThread(); - MOZ_ASSERT(mType == Type_ObjectStore); + MOZ_ASSERT(NS_IsMainThread()); - Reset(); - - mKey = Move(aKey); - mCloneInfo = Move(aValue); - - mHaveValue = !mKey.IsUnset(); -} - -void -IDBCursor::Reset(Key&& aKey) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mType == Type_ObjectStoreKey); - - Reset(); - - mKey = Move(aKey); - - mHaveValue = !mKey.IsUnset(); -} - -void -IDBCursor::Reset(Key&& aKey, - Key&& aPrimaryKey, - StructuredCloneReadInfo&& aValue) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mType == Type_Index); - - Reset(); - - mKey = Move(aKey); - mPrimaryKey = Move(aPrimaryKey); - mCloneInfo = Move(aValue); - - mHaveValue = !mKey.IsUnset(); -} - -void -IDBCursor::Reset(Key&& aKey, Key&& aPrimaryKey) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mType == Type_IndexKey); - - Reset(); - - mKey = Move(aKey); - mPrimaryKey = Move(aPrimaryKey); - - mHaveValue = !mKey.IsUnset(); -} - -NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor) -NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceObjectStore) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceIndex) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) - MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined()); - MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey, - tmp->mCachedPrimaryKey.isUndefined()); - MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined()); - - NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) - // Don't unlink mSourceObjectStore or mSourceIndex or mTransaction! - NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER - tmp->DropJSObjects(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -JSObject* -IDBCursor::WrapObject(JSContext* aCx) -{ - AssertIsOnOwningThread(); - - switch (mType) { - case Type_ObjectStore: - case Type_Index: - return IDBCursorWithValueBinding::Wrap(aCx, this); - - case Type_ObjectStoreKey: - case Type_IndexKey: - return IDBCursorBinding::Wrap(aCx, this); - - default: - MOZ_CRASH("Bad type!"); + if (aCount < 1) { + aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT); + return; } + + Key key; + ContinueInternal(key, int32_t(aCount), aRv); + ENSURE_SUCCESS_VOID(aRv); + +#ifdef IDB_PROFILER_USE_MARKS + { + if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "cursor(%s).advance(%ld)", + "IDBRequest[%llu] MT IDBCursor.advance()", + Request()->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(mDirection), aCount); + } + else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "index(%s).cursor(%s).advance(%ld)", + "IDBRequest[%llu] MT IDBCursor.advance()", + Request()->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(mIndex), + IDB_PROFILER_STRING(mDirection), aCount); + } + } +#endif } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +void +CursorHelper::ReleaseMainThreadObjects() +{ + mCursor = nullptr; + AsyncConnectionHelper::ReleaseMainThreadObjects(); +} + +nsresult +CursorHelper::Dispatch(nsIEventTarget* aDatabaseThread) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("CursorHelper", "Dispatch", + js::ProfileEntry::Category::STORAGE); + + if (IndexedDatabaseManager::IsMainProcess()) { + return AsyncConnectionHelper::Dispatch(aDatabaseThread); + } + + // If we've been invalidated then there's no point sending anything to the + // parent process. + if (mCursor->Transaction()->Database()->IsInvalidated()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IndexedDBCursorChild* cursorActor = mCursor->GetActorChild(); + NS_ASSERTION(cursorActor, "Must have an actor here!"); + + CursorRequestParams params; + nsresult rv = PackArgumentsForParentProcess(params); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NoDispatchEventTarget target; + rv = AsyncConnectionHelper::Dispatch(&target); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mActor = new IndexedDBCursorRequestChild(this, mCursor, params.type()); + cursorActor->SendPIndexedDBRequestConstructor(mActor, params); + + return NS_OK; +} + +nsresult +ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("ContinueHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + // We need to pick a query based on whether or not the cursor's mContinueToKey + // is set. If it is unset then othing was passed to continue so we'll grab the + // next item in the database that is greater than (less than, if we're running + // a PREV cursor) the current key. If it is set then a key was passed to + // continue so we'll grab the next item in the database that is greater than + // (less than, if we're running a PREV cursor) or equal to the key that was + // specified. + + nsAutoCString query; + if (mCursor->mContinueToKey.IsUnset()) { + query.Assign(mCursor->mContinueQuery); + } + else { + query.Assign(mCursor->mContinueToQuery); + } + NS_ASSERTION(!query.IsEmpty(), "Bad query!"); + + query.AppendInt(mCount); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = BindArgumentsToStatement(stmt); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(mCount > 0, "Not ok!"); + + bool hasResult; + for (int32_t index = 0; index < mCount; index++) { + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!hasResult) { + break; + } + } + + if (hasResult) { + rv = GatherResultsFromStatement(stmt); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + mKey.Unset(); + } + + return NS_OK; +} + +nsresult +ContinueHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + UpdateCursorState(); + + if (mKey.IsUnset()) { + aVal.setNull(); + } + else { + nsresult rv = WrapNative(aCx, mCursor, aVal); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +void +ContinueHelper::ReleaseMainThreadObjects() +{ + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + CursorHelper::ReleaseMainThreadObjects(); +} + +nsresult +ContinueHelper::PackArgumentsForParentProcess(CursorRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("ContinueHelper", "PackArgumentsForParentProcess", + js::ProfileEntry::Category::STORAGE); + + ContinueParams params; + + params.key() = mCursor->mContinueToKey; + params.count() = uint32_t(mCount); + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +ContinueHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("ContinueHelper", "SendResponseToChildProcess", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + InfallibleTArray blobsParent; + + if (NS_SUCCEEDED(aResultCode)) { + IDBDatabase* database = mTransaction->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + const nsTArray& files = mCloneReadInfo.mFiles; + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobsParent); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobsToActors failed!"); + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + ContinueResponse continueResponse; + continueResponse.key() = mKey; + continueResponse.objectKey() = mObjectKey; + continueResponse.cloneInfo() = mCloneReadInfo; + continueResponse.blobsParent().SwapElements(blobsParent); + response = continueResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + UpdateCursorState(); + + return Success_Sent; +} + +nsresult +ContinueHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TContinueResponse, + "Bad response type!"); + + const ContinueResponse& response = aResponseValue.get_ContinueResponse(); + + mKey = response.key(); + mObjectKey = response.objectKey(); + + const SerializedStructuredCloneReadInfo& cloneInfo = response.cloneInfo(); + + NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || + (cloneInfo.dataLength && cloneInfo.data), + "Inconsistent clone info!"); + + if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IDBObjectStore::ConvertActorsToBlobs(response.blobsChild(), + mCloneReadInfo.mFiles); + return NS_OK; +} + +nsresult +ContinueObjectStoreHelper::BindArgumentsToStatement( + mozIStorageStatement* aStatement) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aStatement); + + // Bind object store id. + nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mCursor->mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); + NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); + + // Bind current key. + const Key& currentKey = mCursor->mContinueToKey.IsUnset() ? + mCursor->mKey : + mCursor->mContinueToKey; + + rv = currentKey.BindToStatement(aStatement, currentKeyName); + NS_ENSURE_SUCCESS(rv, rv); + + // Bind range key if it is specified. + const Key& rangeKey = mCursor->mRangeKey; + + if (!rangeKey.IsUnset()) { + rv = rangeKey.BindToStatement(aStatement, rangeKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +ContinueObjectStoreHelper::GatherResultsFromStatement( + mozIStorageStatement* aStatement) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aStatement); + + // Figure out what kind of key we have next. + nsresult rv = mKey.SetFromStatement(aStatement, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2, + mDatabase, + mCloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +ContinueObjectStoreKeyHelper::GatherResultsFromStatement( + mozIStorageStatement* aStatement) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aStatement); + + nsresult rv = mKey.SetFromStatement(aStatement, 0); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement) +{ + // Bind index id. + nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mCursor->mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); + + // Bind current key. + const Key& currentKey = mCursor->mContinueToKey.IsUnset() ? + mCursor->mKey : + mCursor->mContinueToKey; + + rv = currentKey.BindToStatement(aStatement, currentKeyName); + NS_ENSURE_SUCCESS(rv, rv); + + // Bind range key if it is specified. + if (!mCursor->mRangeKey.IsUnset()) { + NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); + rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Bind object key if duplicates are allowed and we're not continuing to a + // specific key. + if ((mCursor->mDirection == IDBCursor::NEXT || + mCursor->mDirection == IDBCursor::PREV) && + mCursor->mContinueToKey.IsUnset()) { + NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!"); + + NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key"); + rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +ContinueIndexHelper::GatherResultsFromStatement( + mozIStorageStatement* aStatement) +{ + nsresult rv = mKey.SetFromStatement(aStatement, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mObjectKey.SetFromStatement(aStatement, 1); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +ContinueIndexObjectHelper::GatherResultsFromStatement( + mozIStorageStatement* aStatement) +{ + nsresult rv = mKey.SetFromStatement(aStatement, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mObjectKey.SetFromStatement(aStatement, 1); + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3, + mDatabase, mCloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} diff --git a/dom/indexedDB/IDBCursor.h b/dom/indexedDB/IDBCursor.h index 3d2f52299ef4..3e0b46431bd1 100644 --- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.h @@ -7,38 +7,59 @@ #ifndef mozilla_dom_indexeddb_idbcursor_h__ #define mozilla_dom_indexeddb_idbcursor_h__ -#include "IndexedDatabase.h" -#include "js/RootingAPI.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + #include "mozilla/Attributes.h" #include "mozilla/dom/IDBCursorBinding.h" -#include "mozilla/dom/indexedDB/Key.h" -#include "nsAutoPtr.h" +#include "mozilla/ErrorResult.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" +#include "mozilla/dom/indexedDB/IDBObjectStore.h" +#include "mozilla/dom/indexedDB/Key.h" + +class nsIRunnable; +class nsIScriptContext; class nsPIDOMWindow; namespace mozilla { - -class ErrorResult; - namespace dom { - class OwningIDBObjectStoreOrIDBIndex; +} +} -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE -class BackgroundCursorChild; +class ContinueHelper; +class ContinueObjectStoreHelper; +class ContinueIndexHelper; +class ContinueIndexObjectHelper; class IDBIndex; -class IDBObjectStore; class IDBRequest; class IDBTransaction; +class IndexedDBCursorChild; +class IndexedDBCursorParent; -class IDBCursor MOZ_FINAL - : public nsISupports - , public nsWrapperCache +class IDBCursor MOZ_FINAL : public nsISupports, + public nsWrapperCache { + friend class ContinueHelper; + friend class ContinueObjectStoreHelper; + friend class ContinueIndexHelper; + friend class ContinueIndexObjectHelper; + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor) + + enum Type + { + OBJECTSTORE = 0, + OBJECTSTOREKEY, + INDEXKEY, + INDEXOBJECT + }; + enum Direction { NEXT = 0, @@ -50,84 +71,112 @@ public: DIRECTION_INVALID }; -private: - enum Type - { - Type_ObjectStore, - Type_ObjectStoreKey, - Type_Index, - Type_IndexKey, - }; - - nsRefPtr mSourceObjectStore; - nsRefPtr mSourceIndex; - nsRefPtr mTransaction; - - BackgroundCursorChild* mBackgroundActor; - - JS::Heap mScriptOwner; - - // These are cycle-collected! - JS::Heap mCachedKey; - JS::Heap mCachedPrimaryKey; - JS::Heap mCachedValue; - - Key mKey; - Key mPrimaryKey; - StructuredCloneReadInfo mCloneInfo; - - const Type mType; - const Direction mDirection; - - bool mHaveCachedKey : 1; - bool mHaveCachedPrimaryKey : 1; - bool mHaveCachedValue : 1; - bool mRooted : 1; - bool mContinueCalled : 1; - bool mHaveValue : 1; - -public: - static already_AddRefed - Create(IDBObjectStore* aObjectStore, - BackgroundCursorChild* aBackgroundActor, + // For OBJECTSTORE cursors. + static + already_AddRefed + Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey, - StructuredCloneReadInfo&& aCloneInfo); + StructuredCloneReadInfo&& aCloneReadInfo); - static already_AddRefed - Create(IDBObjectStore* aObjectStore, - BackgroundCursorChild* aBackgroundActor, + // For OBJECTSTOREKEY cursors. + static + already_AddRefed + Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey); - static already_AddRefed - Create(IDBIndex* aIndex, - BackgroundCursorChild* aBackgroundActor, + // For INDEXKEY cursors. + static + already_AddRefed + Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBIndex* aIndex, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey, - const Key& aPrimaryKey, - StructuredCloneReadInfo&& aCloneInfo); + const Key& aObjectKey); - static already_AddRefed - Create(IDBIndex* aIndex, - BackgroundCursorChild* aBackgroundActor, + // For INDEXOBJECT cursors. + static + already_AddRefed + Create(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBIndex* aIndex, Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery, const Key& aKey, - const Key& aPrimaryKey); + const Key& aObjectKey, + StructuredCloneReadInfo&& aCloneReadInfo); + + IDBTransaction* Transaction() const + { + return mTransaction; + } + + IDBRequest* Request() const + { + return mRequest; + } static Direction ConvertDirection(IDBCursorDirection aDirection); void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif + SetActor(IndexedDBCursorChild* aActorChild) + { + NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); + mActorChild = aActorChild; + } - nsPIDOMWindow* - GetParentObject() const; + void + SetActor(IndexedDBCursorParent* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, + "Shouldn't have more than one!"); + mActorParent = aActorParent; + } + + IndexedDBCursorChild* + GetActorChild() const + { + return mActorChild; + } + + IndexedDBCursorParent* + GetActorParent() const + { + return mActorParent; + } + + void + ContinueInternal(const Key& aKey, int32_t aCount, + ErrorResult& aRv); + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // WebIDL + IDBTransaction* + GetParentObject() const + { + return mTransaction; + } void GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const; @@ -136,79 +185,80 @@ public: GetDirection() const; void - GetKey(JSContext* aCx, - JS::MutableHandle aResult, + GetKey(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv); void - GetPrimaryKey(JSContext* aCx, - JS::MutableHandle aResult, + GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv); - void - GetValue(JSContext* aCx, - JS::MutableHandle aResult, - ErrorResult& aRv); - - void - Continue(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); + already_AddRefed + Update(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv); void Advance(uint32_t aCount, ErrorResult& aRv); - already_AddRefed - Update(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv); + void + Continue(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); already_AddRefed Delete(JSContext* aCx, ErrorResult& aRv); void - Reset(); - - void - Reset(Key&& aKey, StructuredCloneReadInfo&& aValue); - - void - Reset(Key&& aKey); - - void - Reset(Key&& aKey, Key&& aPrimaryKey, StructuredCloneReadInfo&& aValue); - - void - Reset(Key&& aKey, Key&& aPrimaryKey); - - void - ClearBackgroundActor() - { - AssertIsOnOwningThread(); - - mBackgroundActor = nullptr; - } - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor) - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - -private: - IDBCursor(Type aType, - IDBObjectStore* aSourceObjectStore, - IDBIndex* aSourceIndex, - IDBTransaction* aTransaction, - BackgroundCursorChild* aBackgroundActor, - Direction aDirection, - const Key& aKey); + GetValue(JSContext* aCx, JS::MutableHandle aResult, + ErrorResult& aRv); +protected: + IDBCursor(); ~IDBCursor(); - void - DropJSObjects(); + void DropJSObjects(); + + static + already_AddRefed + CreateCommon(IDBRequest* aRequest, + IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, + Direction aDirection, + const Key& aRangeKey, + const nsACString& aContinueQuery, + const nsACString& aContinueToQuery); + + nsRefPtr mRequest; + nsRefPtr mTransaction; + nsRefPtr mObjectStore; + nsRefPtr mIndex; + + JS::Heap mScriptOwner; + + Type mType; + Direction mDirection; + nsCString mContinueQuery; + nsCString mContinueToQuery; + + // These are cycle-collected! + JS::Heap mCachedKey; + JS::Heap mCachedPrimaryKey; + JS::Heap mCachedValue; + + Key mRangeKey; + + Key mKey; + Key mObjectKey; + StructuredCloneReadInfo mCloneReadInfo; + Key mContinueToKey; + + IndexedDBCursorChild* mActorChild; + IndexedDBCursorParent* mActorParent; + + bool mHaveCachedKey; + bool mHaveCachedPrimaryKey; + bool mHaveCachedValue; + bool mRooted; + bool mContinueCalled; + bool mHaveValue; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbcursor_h__ diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 6f3eb410032b..7fb922b8d486 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -4,507 +4,464 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "base/basictypes.h" + #include "IDBDatabase.h" -#include "FileInfo.h" -#include "FileManager.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/Mutex.h" +#include "mozilla/storage.h" +#include "mozilla/dom/nsIContentParent.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/dom/DOMStringListBinding.h" +#include "mozilla/dom/quota/Client.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "nsJSUtils.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" + +#include "AsyncConnectionHelper.h" +#include "DatabaseInfo.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IDBIndex.h" #include "IDBMutableFile.h" #include "IDBObjectStore.h" -#include "IDBRequest.h" #include "IDBTransaction.h" #include "IDBFactory.h" -#include "IndexedDatabaseManager.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/EventDispatcher.h" -#include "MainThreadUtils.h" -#include "mozilla/Services.h" -#include "mozilla/storage.h" -#include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/dom/DOMStringList.h" -#include "mozilla/dom/DOMStringListBinding.h" -#include "mozilla/dom/IDBDatabaseBinding.h" -#include "mozilla/dom/IDBObjectStoreBinding.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" -#include "mozilla/ipc/BackgroundUtils.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/nsIRemoteBlob.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/ipc/FileDescriptor.h" -#include "mozilla/ipc/InputStreamParams.h" -#include "mozilla/ipc/InputStreamUtils.h" -#include "nsCOMPtr.h" -#include "nsDOMFile.h" -#include "nsIDocument.h" -#include "nsIDOMFile.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" -#include "nsISupportsPrimitives.h" -#include "nsThreadUtils.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" +#include "TransactionThreadPool.h" -// Include this last to avoid path problems on Windows. -#include "ActorsChild.h" +#include "ipc/IndexedDBChild.h" +#include "ipc/IndexedDBParent.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "mozilla/dom/IDBDatabaseBinding.h" -using namespace mozilla::dom::quota; -using namespace mozilla::ipc; -using namespace mozilla::services; +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::nsIContentParent; +using mozilla::dom::quota::AssertIsOnIOThread; +using mozilla::dom::quota::Client; +using mozilla::dom::quota::QuotaManager; +using mozilla::ErrorResult; +using namespace mozilla; +using namespace mozilla::dom; namespace { -const char kCycleCollectionObserverTopic[] = "cycle-collector-end"; -const char kMemoryPressureObserverTopic[] = "memory-pressure"; -const char kWindowObserverTopic[] = "inner-window-destroyed"; - -// XXX This should either be ported to PBackground or removed someday. -class CreateFileHelper MOZ_FINAL - : public nsRunnable +class NoRequestDatabaseHelper : public AsyncConnectionHelper { - nsRefPtr mDatabase; - nsRefPtr mRequest; - nsRefPtr mFileInfo; - - const nsString mName; - const nsString mType; - const nsString mDatabaseName; - const nsCString mOrigin; - - const PersistenceType mPersistenceType; - - nsresult mResultCode; - public: - static nsresult - CreateAndDispatch(IDBDatabase* aDatabase, - IDBRequest* aRequest, - const nsAString& aName, - const nsAString& aType); + explicit NoRequestDatabaseHelper(IDBTransaction* aTransaction) + : AsyncConnectionHelper(aTransaction, nullptr) + { + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aTransaction, "Null transaction!"); + } - NS_DECL_ISUPPORTS_INHERITED + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + + virtual nsresult OnSuccess() MOZ_OVERRIDE; + + virtual void OnError() MOZ_OVERRIDE; +}; + +class CreateObjectStoreHelper : public NoRequestDatabaseHelper +{ +public: + CreateObjectStoreHelper(IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore) + : NoRequestDatabaseHelper(aTransaction), mObjectStore(aObjectStore) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; private: + nsRefPtr mObjectStore; +}; + +class DeleteObjectStoreHelper : public NoRequestDatabaseHelper +{ +public: + DeleteObjectStoreHelper(IDBTransaction* aTransaction, + int64_t aObjectStoreId) + : NoRequestDatabaseHelper(aTransaction), mObjectStoreId(aObjectStoreId) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + +private: + // In-params. + int64_t mObjectStoreId; +}; + +class CreateFileHelper : public AsyncConnectionHelper +{ +public: CreateFileHelper(IDBDatabase* aDatabase, IDBRequest* aRequest, const nsAString& aName, - const nsAString& aType, - const nsACString& aOrigin); + const nsAString& aType) + : AsyncConnectionHelper(aDatabase, aRequest), + mName(aName), mType(aType) + { } ~CreateFileHelper() + { } + + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); + nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal); + void ReleaseMainThreadObjects() { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + mFileInfo = nullptr; + AsyncConnectionHelper::ReleaseMainThreadObjects(); } - nsresult - DoDatabaseWork(); - - void - DoMainThreadWork(nsresult aResultCode); - - NS_DECL_NSIRUNNABLE -}; - -class DatabaseFile MOZ_FINAL - : public PBackgroundIDBDatabaseFileChild -{ - IDBDatabase* mDatabase; - -public: - explicit DatabaseFile(IDBDatabase* aDatabase) - : mDatabase(aDatabase) + virtual ChildProcessSendResult SendResponseToChildProcess( + nsresult aResultCode) + MOZ_OVERRIDE { - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); + return Success_NotSent; + } - MOZ_COUNT_CTOR(DatabaseFile); + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE + { + MOZ_CRASH("Should never get here!"); } private: - ~DatabaseFile() + // In-params. + nsString mName; + nsString mType; + + // Out-params. + nsRefPtr mFileInfo; +}; + +class MOZ_STACK_CLASS AutoRemoveObjectStore +{ +public: + AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName) + : mInfo(aInfo), mName(aName) + { } + + ~AutoRemoveObjectStore() { - MOZ_ASSERT(!mDatabase); - - MOZ_COUNT_DTOR(DatabaseFile); - } - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE - { - MOZ_ASSERT(mDatabase); - mDatabase->AssertIsOnOwningThread(); - - if (aWhy != Deletion) { - nsRefPtr database = mDatabase; - database->NoteFinishedFileActor(this); + if (mInfo) { + mInfo->RemoveObjectStore(mName); } - -#ifdef DEBUG - mDatabase = nullptr; -#endif } + + void forget() + { + mInfo = nullptr; + } + +private: + DatabaseInfo* mInfo; + nsString mName; }; } // anonymous namespace -class IDBDatabase::Observer MOZ_FINAL - : public nsIObserver -{ - IDBDatabase* mWeakDatabase; - const uint64_t mWindowId; - -public: - Observer(IDBDatabase* aDatabase, uint64_t aWindowId) - : mWeakDatabase(aDatabase) - , mWindowId(aWindowId) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDatabase); - } - - void - Revoke() - { - MOZ_ASSERT(NS_IsMainThread()); - - mWeakDatabase = nullptr; - } - - NS_DECL_ISUPPORTS - -private: - ~Observer() - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mWeakDatabase); - } - - NS_DECL_NSIOBSERVER -}; - -IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache, - IDBFactory* aFactory, - BackgroundDatabaseChild* aActor, - DatabaseSpec* aSpec) - : IDBWrapperCache(aOwnerCache) - , mFactory(aFactory) - , mSpec(aSpec) - , mBackgroundActor(aActor) - , mClosed(false) - , mInvalidated(false) -{ - MOZ_ASSERT(aOwnerCache); - MOZ_ASSERT(aFactory); - aFactory->AssertIsOnOwningThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aSpec); - - SetIsDOMBinding(); -} - -IDBDatabase::~IDBDatabase() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mBackgroundActor); -} - // static already_AddRefed IDBDatabase::Create(IDBWrapperCache* aOwnerCache, IDBFactory* aFactory, - BackgroundDatabaseChild* aActor, - DatabaseSpec* aSpec) + already_AddRefed aDatabaseInfo, + const nsACString& aASCIIOrigin, + FileManager* aFileManager, + mozilla::dom::nsIContentParent* aContentParent) { - MOZ_ASSERT(aOwnerCache); - MOZ_ASSERT(aFactory); - aFactory->AssertIsOnOwningThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aSpec); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aFactory, "Null pointer!"); + NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!"); - nsRefPtr db = - new IDBDatabase(aOwnerCache, aFactory, aActor, aSpec); + nsRefPtr databaseInfo(aDatabaseInfo); + NS_ASSERTION(databaseInfo, "Null pointer!"); + + nsRefPtr db(new IDBDatabase(aOwnerCache)); db->SetScriptOwner(aOwnerCache->GetScriptOwner()); + db->mFactory = aFactory; + db->mDatabaseId = databaseInfo->id; + db->mName = databaseInfo->name; + db->mFilePath = databaseInfo->filePath; + db->mPersistenceType = databaseInfo->persistenceType; + db->mGroup = databaseInfo->group; + databaseInfo.swap(db->mDatabaseInfo); + db->mASCIIOrigin = aASCIIOrigin; + db->mFileManager = aFileManager; + db->mContentParent = aContentParent; - if (NS_IsMainThread()) { - if (nsPIDOMWindow* window = aFactory->GetParentObject()) { - MOZ_ASSERT(window->IsInnerWindow()); + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); - uint64_t windowId = window->WindowID(); + db->mQuotaClient = quotaManager->GetClient(Client::IDB); + NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!"); - nsRefPtr observer = new Observer(db, windowId); - - nsCOMPtr obsSvc = GetObserverService(); - MOZ_ASSERT(obsSvc); - - // This topic must be successfully registered. - if (NS_WARN_IF(NS_FAILED( - obsSvc->AddObserver(observer, kWindowObserverTopic, false)))) { - observer->Revoke(); - return nullptr; - } - - // These topics are not crucial. - if (NS_FAILED(obsSvc->AddObserver(observer, - kCycleCollectionObserverTopic, - false)) || - NS_FAILED(obsSvc->AddObserver(observer, - kMemoryPressureObserverTopic, - false))) { - NS_WARNING("Failed to add additional memory observers!"); - } - - db->mObserver.swap(observer); - } + if (!quotaManager->RegisterStorage(db)) { + // Either out of memory or shutting down. + return nullptr; } + db->mRegistered = true; + return db.forget(); } -#ifdef DEBUG - -void -IDBDatabase::AssertIsOnOwningThread() const +// static +IDBDatabase* +IDBDatabase::FromStorage(nsIOfflineStorage* aStorage) { - MOZ_ASSERT(mFactory); - mFactory->AssertIsOnOwningThread(); + return aStorage->GetClient()->GetType() == Client::IDB ? + static_cast(aStorage) : nullptr; } -#endif // DEBUG +IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache) +: IDBWrapperCache(aOwnerCache), + mActorChild(nullptr), + mActorParent(nullptr), + mContentParent(nullptr), + mInvalidated(false), + mRegistered(false), + mClosed(false), + mRunningVersionChange(false) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} + +IDBDatabase::~IDBDatabase() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} void -IDBDatabase::CloseInternal() +IDBDatabase::LastRelease() { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (!mClosed) { - mClosed = true; + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->Send__delete__(mActorChild); + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); + } - ExpireFileActors(/* aExpireAll */ true); + if (mRegistered) { + CloseInternal(true); - if (mObserver) { - mObserver->Revoke(); - - nsCOMPtr obsSvc = GetObserverService(); - if (obsSvc) { - // These might not have been registered. - obsSvc->RemoveObserver(mObserver, kCycleCollectionObserverTopic); - obsSvc->RemoveObserver(mObserver, kMemoryPressureObserverTopic); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - obsSvc->RemoveObserver(mObserver, kWindowObserverTopic))); - } - - mObserver = nullptr; + QuotaManager* quotaManager = QuotaManager::Get(); + if (quotaManager) { + quotaManager->UnregisterStorage(this); } + mRegistered = false; + } +} - if (mBackgroundActor && !mInvalidated) { - mBackgroundActor->SendClose(); - } +NS_IMETHODIMP_(void) +IDBDatabase::Invalidate() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + InvalidateInternal(/* aIsDead */ false); +} + +void +IDBDatabase::InvalidateInternal(bool aIsDead) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (IsInvalidated()) { + return; + } + + mInvalidated = true; + + // Make sure we're closed too. + Close(); + + // When the IndexedDatabaseManager needs to invalidate databases, all it has + // is an origin, so we call into the quota manager here to cancel any prompts + // for our owner. + nsPIDOMWindow* owner = GetOwner(); + if (owner) { + QuotaManager::CancelPromptsForWindow(owner); + } + + // We want to forcefully remove in the child when the parent has invalidated + // us in IPC mode because the database might no longer exist. + // We don't want to forcefully remove in the parent when a child dies since + // other child processes may be using the referenced DatabaseInfo. + if (!aIsDead) { + DatabaseInfo::Remove(mDatabaseId); + } + + // And let the child process know as well. + if (mActorParent) { + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorParent->Invalidate(); } } void -IDBDatabase::InvalidateInternal() +IDBDatabase::DisconnectFromActorParent() { - AssertIsOnOwningThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - InvalidateMutableFiles(); - AbortTransactions(); + // Make sure we're closed too. + Close(); - CloseInternal(); + // Kill any outstanding prompts. + nsPIDOMWindow* owner = GetOwner(); + if (owner) { + QuotaManager::CancelPromptsForWindow(owner); + } } void -IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion) +IDBDatabase::CloseInternal(bool aIsDead) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aNewVersion); - MOZ_ASSERT(!RunningVersionChangeTransaction()); - MOZ_ASSERT(mSpec); - MOZ_ASSERT(!mPreviousSpec); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - mPreviousSpec = new DatabaseSpec(*mSpec); + if (!mClosed) { + mClosed = true; - mSpec->metadata().version() = aNewVersion; + // If we're getting called from Unlink, avoid cloning the DatabaseInfo. + { + nsRefPtr previousInfo; + mDatabaseInfo.swap(previousInfo); + + if (!aIsDead) { + mDatabaseInfo = previousInfo->Clone(); + } + } + + QuotaManager* quotaManager = QuotaManager::Get(); + if (quotaManager) { + quotaManager->OnStorageClosed(this); + } + + // And let the parent process know as well. + if (mActorChild && !IsInvalidated()) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->SendClose(aIsDead); + } + } +} + +NS_IMETHODIMP_(bool) +IDBDatabase::IsClosed() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mClosed; +} + +void +IDBDatabase::EnterSetVersionTransaction() +{ + NS_ASSERTION(!mRunningVersionChange, "How did that happen?"); + + mPreviousDatabaseInfo = mDatabaseInfo->Clone(); + + mRunningVersionChange = true; } void IDBDatabase::ExitSetVersionTransaction() { - AssertIsOnOwningThread(); + NS_ASSERTION(mRunningVersionChange, "How did that happen?"); - if (mPreviousSpec) { - mPreviousSpec = nullptr; - } + mPreviousDatabaseInfo = nullptr; + + mRunningVersionChange = false; } void IDBDatabase::RevertToPreviousState() { - AssertIsOnOwningThread(); - MOZ_ASSERT(RunningVersionChangeTransaction()); - MOZ_ASSERT(mPreviousSpec); - - // Hold the current spec alive until RefreshTransactionsSpecEnumerator has - // finished! - nsAutoPtr currentSpec(mSpec.forget()); - - mSpec = mPreviousSpec.forget(); - - RefreshSpec(/* aMayDelete */ true); + mDatabaseInfo = mPreviousDatabaseInfo; + mPreviousDatabaseInfo = nullptr; } void -IDBDatabase::RefreshSpec(bool aMayDelete) +IDBDatabase::OnUnlink() { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static PLDHashOperator - RefreshTransactionsSpec(nsPtrHashKey* aTransaction, - void* aClosure) - { - MOZ_ASSERT(aTransaction); - aTransaction->GetKey()->AssertIsOnOwningThread(); - MOZ_ASSERT(aClosure); + // We've been unlinked, at the very least we should be able to prevent further + // transactions from starting and unblock any other SetVersion callers. + CloseInternal(true); - bool mayDelete = *static_cast(aClosure); + // No reason for the QuotaManager to track us any longer. + QuotaManager* quotaManager = QuotaManager::Get(); + if (mRegistered && quotaManager) { + quotaManager->UnregisterStorage(this); - nsRefPtr transaction = aTransaction->GetKey(); - transaction->RefreshSpec(mayDelete); - - return PL_DHASH_NEXT; - } - }; - - mTransactions.EnumerateEntries(Helper::RefreshTransactionsSpec, &aMayDelete); -} - -nsPIDOMWindow* -IDBDatabase::GetParentObject() const -{ - return mFactory->GetParentObject(); -} - -const nsString& -IDBDatabase::Name() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); - - return mSpec->metadata().name(); -} - -uint64_t -IDBDatabase::Version() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); - - return mSpec->metadata().version(); -} - -already_AddRefed -IDBDatabase::ObjectStoreNames() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); - - const nsTArray& objectStores = mSpec->objectStores(); - - nsRefPtr list = new DOMStringList(); - - if (!objectStores.IsEmpty()) { - nsTArray& listNames = list->StringArray(); - listNames.SetCapacity(objectStores.Length()); - - for (uint32_t index = 0; index < objectStores.Length(); index++) { - listNames.InsertElementSorted(objectStores[index].metadata().name()); - } + // Don't try to unregister again in the destructor. + mRegistered = false; } - - return list.forget(); -} - -already_AddRefed -IDBDatabase::GetOwnerDocument() const -{ - if (nsPIDOMWindow* window = GetOwner()) { - nsCOMPtr doc = window->GetExtantDoc(); - return doc.forget(); - } - return nullptr; } already_AddRefed -IDBDatabase::CreateObjectStore( - JSContext* aCx, - const nsAString& aName, - const IDBObjectStoreParameters& aOptionalParameters, - ErrorResult& aRv) +IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction, + const ObjectStoreInfoGuts& aInfo, + ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "Null transaction!"); - IDBTransaction* transaction = IDBTransaction::GetCurrent(); + DatabaseInfo* databaseInfo = aTransaction->DBInfo(); - if (!transaction || - transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + nsRefPtr newInfo = new ObjectStoreInfo(); + *static_cast(newInfo.get()) = aInfo; + + newInfo->nextAutoIncrementId = aInfo.autoIncrement ? 1 : 0; + newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; + + if (!databaseInfo->PutObjectStore(newInfo)) { + IDB_WARNING("Put failed!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - MOZ_ASSERT(transaction->IsOpen()); + // Don't leave this in the hash if we fail below! + AutoRemoveObjectStore autoRemove(databaseInfo, newInfo->name); - KeyPath keyPath(0); - if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + nsRefPtr objectStore = + aTransaction->GetOrCreateObjectStore(newInfo->name, newInfo, true); + if (!objectStore) { + IDB_WARNING("Failed to get objectStore!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - nsTArray& objectStores = mSpec->objectStores(); - for (uint32_t count = objectStores.Length(), index = 0; - index < count; - index++) { - if (aName == objectStores[index].metadata().name()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); + if (IndexedDatabaseManager::IsMainProcess()) { + nsRefPtr helper = + new CreateObjectStoreHelper(aTransaction, objectStore); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } } - if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { - aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); - return nullptr; - } - - const ObjectStoreSpec* oldSpecElements = - objectStores.IsEmpty() ? nullptr : objectStores.Elements(); - - ObjectStoreSpec* newSpec = objectStores.AppendElement(); - newSpec->metadata() = - ObjectStoreMetadata(transaction->NextObjectStoreId(), nsString(aName), - keyPath, aOptionalParameters.mAutoIncrement); - - if (oldSpecElements && - oldSpecElements != objectStores.Elements()) { - MOZ_ASSERT(objectStores.Length() > 1); - - // Array got moved, update the spec pointers for all live objectStores and - // indexes. - RefreshSpec(/* aMayDelete */ false); - } - - nsRefPtr objectStore = - transaction->CreateObjectStore(*newSpec); - MOZ_ASSERT(objectStore); + autoRemove.forget(); IDB_PROFILER_MARK("IndexedDB Pseudo-request: " "database(%s).transaction(%s).createObjectStore(%s)", @@ -516,12 +473,107 @@ IDBDatabase::CreateObjectStore( return objectStore.forget(); } +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) + // Don't unlink mFactory! + + // Do some cleanup. + tmp->OnUnlink(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) + NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) + +NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) + +JSObject* +IDBDatabase::WrapObject(JSContext* aCx) +{ + return IDBDatabaseBinding::Wrap(aCx, this); +} + +uint64_t +IDBDatabase::Version() const +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + DatabaseInfo* info = Info(); + return info->version; +} + +already_AddRefed +IDBDatabase::GetObjectStoreNames(ErrorResult& aRv) const +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + DatabaseInfo* info = Info(); + + nsRefPtr list(new DOMStringList()); + if (!info->GetObjectStoreNames(list->StringArray())) { + IDB_WARNING("Couldn't get names!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + return list.forget(); +} + +already_AddRefed +IDBDatabase::CreateObjectStore( + JSContext* aCx, const nsAString& aName, + const IDBObjectStoreParameters& aOptionalParameters, + ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); + + if (!transaction || + transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + DatabaseInfo* databaseInfo = transaction->DBInfo(); + + KeyPath keyPath(0); + if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + + if (databaseInfo->ContainsStoreName(aName)) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); + return nullptr; + } + + if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return nullptr; + } + + ObjectStoreInfoGuts guts; + + guts.name = aName; + guts.id = databaseInfo->nextObjectStoreId++; + guts.keyPath = keyPath; + guts.autoIncrement = aOptionalParameters.mAutoIncrement; + + return CreateObjectStoreInternal(transaction, guts, aRv); +} + void IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - IDBTransaction* transaction = IDBTransaction::GetCurrent(); + IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); if (!transaction || transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { @@ -529,36 +581,33 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) return; } - MOZ_ASSERT(transaction->IsOpen()); - - nsTArray& specArray = mSpec->objectStores(); - - int64_t objectStoreId = 0; - - for (uint32_t specCount = specArray.Length(), specIndex = 0; - specIndex < specCount; - specIndex++) { - const ObjectStoreMetadata& metadata = specArray[specIndex].metadata(); - MOZ_ASSERT(metadata.id()); - - if (aName == metadata.name()) { - objectStoreId = metadata.id(); - - // Must do this before altering the metadata array! - transaction->DeleteObjectStore(objectStoreId); - - specArray.RemoveElementAt(specIndex); - - RefreshSpec(/* aMayDelete */ false); - break; - } - } - - if (!objectStoreId) { + DatabaseInfo* info = transaction->DBInfo(); + ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName); + if (!objectStoreInfo) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return; } + if (IndexedDatabaseManager::IsMainProcess()) { + nsRefPtr helper = + new DeleteObjectStoreHelper(transaction, objectStoreInfo->id); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return; + } + } + else { + IndexedDBTransactionChild* actor = transaction->GetActorChild(); + NS_ASSERTION(actor, "Must have an actor here!"); + + actor->SendDeleteObjectStore(nsString(aName)); + } + + transaction->RemoveObjectStore(aName); + IDB_PROFILER_MARK("IndexedDB Pseudo-request: " "database(%s).transaction(%s).deleteObjectStore(\"%s\")", "MT IDBDatabase.deleteObjectStore()", @@ -567,28 +616,11 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) NS_ConvertUTF16toUTF8(aName).get()); } -already_AddRefed -IDBDatabase::Transaction(const nsAString& aStoreName, - IDBTransactionMode aMode, - ErrorResult& aRv) -{ - AssertIsOnOwningThread(); - - Sequence storeNames; - if (!storeNames.AppendElement(aStoreName)) { - aRv.Throw(NS_ERROR_OUT_OF_MEMORY); - return nullptr; - } - - return Transaction(storeNames, aMode, aRv); -} - -already_AddRefed +already_AddRefed IDBDatabase::Transaction(const Sequence& aStoreNames, - IDBTransactionMode aMode, - ErrorResult& aRv) + IDBTransactionMode aMode, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (QuotaManager::IsShuttingDown()) { IDB_REPORT_INTERNAL_ERR(); @@ -601,7 +633,7 @@ IDBDatabase::Transaction(const Sequence& aStoreNames, return nullptr; } - if (RunningVersionChangeTransaction()) { + if (mRunningVersionChange) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } @@ -611,70 +643,37 @@ IDBDatabase::Transaction(const Sequence& aStoreNames, return nullptr; } - IDBTransaction::Mode mode; + IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY; switch (aMode) { case IDBTransactionMode::Readonly: - mode = IDBTransaction::READ_ONLY; + transactionMode = IDBTransaction::READ_ONLY; break; case IDBTransactionMode::Readwrite: - mode = IDBTransaction::READ_WRITE; + transactionMode = IDBTransaction::READ_WRITE; break; case IDBTransactionMode::Versionchange: - aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); - return nullptr; + transactionMode = IDBTransaction::VERSION_CHANGE; + break; default: MOZ_CRASH("Unknown mode!"); } - const nsTArray& objectStores = mSpec->objectStores(); - const uint32_t nameCount = aStoreNames.Length(); - - nsTArray sortedStoreNames; - sortedStoreNames.SetCapacity(nameCount); - - // Check to make sure the object store names we collected actually exist. - for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) { - const nsString& name = aStoreNames[nameIndex]; - - bool found = false; - - for (uint32_t objCount = objectStores.Length(), objIndex = 0; - objIndex < objCount; - objIndex++) { - if (objectStores[objIndex].metadata().name() == name) { - found = true; - break; - } - } - - if (!found) { + // Now check to make sure the object store names we collected actually exist. + DatabaseInfo* info = Info(); + for (uint32_t index = 0; index < aStoreNames.Length(); index++) { + if (!info->ContainsStoreName(aStoreNames[index])) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return nullptr; } - - sortedStoreNames.InsertElementSorted(name); - } - - // Remove any duplicates. - for (uint32_t nameIndex = nameCount - 1; nameIndex > 0; nameIndex--) { - if (sortedStoreNames[nameIndex] == sortedStoreNames[nameIndex - 1]) { - sortedStoreNames.RemoveElementAt(nameIndex); - } } nsRefPtr transaction = - IDBTransaction::Create(this, sortedStoreNames, mode); - MOZ_ASSERT(transaction); - - BackgroundTransactionChild* actor = - new BackgroundTransactionChild(transaction); - - MOZ_ALWAYS_TRUE( - mBackgroundActor->SendPBackgroundIDBTransactionConstructor(actor, - sortedStoreNames, - mode)); - - transaction->SetBackgroundActor(actor); + IDBTransaction::Create(this, aStoreNames, transactionMode, false); + if (!transaction) { + IDB_WARNING("Failed to create the transaction!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)", "IDBTransaction[%llu] MT Started", @@ -684,22 +683,15 @@ IDBDatabase::Transaction(const Sequence& aStoreNames, return transaction.forget(); } -StorageType -IDBDatabase::Storage() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); - - return PersistenceTypeToStorage(mSpec->metadata().persistenceType()); -} - already_AddRefed IDBDatabase::CreateMutableFile(const nsAString& aName, const Optional& aType, ErrorResult& aRv) { - if (!IndexedDatabaseManager::IsMainProcess() || !NS_IsMainThread()) { - IDB_WARNING("Not supported!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!IndexedDatabaseManager::IsMainProcess()) { + IDB_WARNING("Not supported yet!"); aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } @@ -717,474 +709,57 @@ IDBDatabase::CreateMutableFile(const nsAString& aName, nsRefPtr request = IDBRequest::Create(this, nullptr); - nsString type; - if (aType.WasPassed()) { - type = aType.Value(); - } + nsRefPtr helper = + new CreateFileHelper(this, request, aName, + aType.WasPassed() ? aType.Value() : EmptyString()); - aRv = CreateFileHelper::CreateAndDispatch(this, request, aName, type); - if (NS_WARN_IF(aRv.Failed())) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "We should definitely have a manager here"); + + nsresult rv = helper->Dispatch(quotaManager->IOThread()); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } return request.forget(); } -void -IDBDatabase::RegisterTransaction(IDBTransaction* aTransaction) +NS_IMETHODIMP +IDBDatabase::Close() { - AssertIsOnOwningThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - MOZ_ASSERT(!mTransactions.Contains(aTransaction)); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - mTransactions.PutEntry(aTransaction); + CloseInternal(false); + + NS_ASSERTION(mClosed, "Should have set the closed flag!"); + + return NS_OK; } -void -IDBDatabase::UnregisterTransaction(IDBTransaction* aTransaction) +NS_IMETHODIMP_(const nsACString&) +IDBDatabase::Id() { - AssertIsOnOwningThread(); - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - MOZ_ASSERT(mTransactions.Contains(aTransaction)); - - mTransactions.RemoveEntry(aTransaction); + return mDatabaseId; } -void -IDBDatabase::AbortTransactions() +NS_IMETHODIMP_(mozilla::dom::quota::Client*) +IDBDatabase::GetClient() { - AssertIsOnOwningThread(); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static void - AbortTransactions(nsTHashtable>& aTable) - { - const uint32_t count = aTable.Count(); - if (!count) { - return; - } - - nsTArray> transactions; - transactions.SetCapacity(count); - - aTable.EnumerateEntries(Collect, &transactions); - - MOZ_ASSERT(transactions.Length() == count); - - IDB_REPORT_INTERNAL_ERR(); - - for (uint32_t index = 0; index < count; index++) { - nsRefPtr transaction = transactions[index].forget(); - MOZ_ASSERT(transaction); - - transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - } - - private: - static PLDHashOperator - Collect(nsPtrHashKey* aTransaction, void* aClosure) - { - MOZ_ASSERT(aTransaction); - aTransaction->GetKey()->AssertIsOnOwningThread(); - MOZ_ASSERT(aClosure); - - auto* array = static_cast>*>(aClosure); - array->AppendElement(aTransaction->GetKey()); - - return PL_DHASH_NEXT; - } - }; - - Helper::AbortTransactions(mTransactions); + return mQuotaClient; } -PBackgroundIDBDatabaseFileChild* -IDBDatabase::GetOrCreateFileActorForBlob(nsIDOMBlob* aBlob) +NS_IMETHODIMP_(bool) +IDBDatabase::IsOwned(nsPIDOMWindow* aOwner) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aBlob); - MOZ_ASSERT(mBackgroundActor); - - // We use the DOMFile's nsIWeakReference as the key to the table because - // a) it is unique per blob, b) it is reference-counted so that we can - // guarantee that it stays alive, and c) it doesn't hold the actual DOMFile - // alive. - nsCOMPtr weakRef = do_GetWeakReference(aBlob); - MOZ_ASSERT(weakRef); - - PBackgroundIDBDatabaseFileChild* actor = nullptr; - - if (!mFileActors.Get(weakRef, &actor)) { - DOMFileImpl* blobImpl = static_cast(aBlob)->Impl(); - MOZ_ASSERT(blobImpl); - - if (mReceivedBlobs.GetEntry(weakRef)) { - // This blob was previously retrieved from the database. - nsCOMPtr remoteBlob = do_QueryObject(blobImpl); - MOZ_ASSERT(remoteBlob); - - BlobChild* blobChild = remoteBlob->GetBlobChild(); - MOZ_ASSERT(blobChild); - -#ifdef DEBUG - { - PBackgroundChild* backgroundManager = blobChild->GetBackgroundManager(); - MOZ_ASSERT(backgroundManager); - - PBackgroundChild* thisManager = mBackgroundActor->Manager()->Manager(); - MOZ_ASSERT(thisManager); - - MOZ_ASSERT(thisManager == backgroundManager); - } -#endif - auto* dbFile = new DatabaseFile(this); - - actor = - mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(dbFile, - blobChild); - if (NS_WARN_IF(!actor)) { - return nullptr; - } - } else { - nsCOMPtr inputStream; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - blobImpl->GetInternalStream(getter_AddRefs(inputStream)))); - - InputStreamParams params; - nsTArray fileDescriptors; - SerializeInputStream(inputStream, params, fileDescriptors); - - MOZ_ASSERT(fileDescriptors.IsEmpty()); - - auto* dbFile = new DatabaseFile(this); - - actor = - mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(dbFile, - params); - if (NS_WARN_IF(!actor)) { - return nullptr; - } - } - - MOZ_ASSERT(actor); - - mFileActors.Put(weakRef, actor); - } - - MOZ_ASSERT(actor); - - return actor; + return GetOwner() == aOwner; } -void -IDBDatabase::NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor) +NS_IMETHODIMP_(const nsACString&) +IDBDatabase::Origin() { - AssertIsOnOwningThread(); - MOZ_ASSERT(aFileActor); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static PLDHashOperator - Remove(nsISupports* aKey, - PBackgroundIDBDatabaseFileChild*& aValue, - void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - if (aValue == static_cast(aClosure)) { - return PL_DHASH_REMOVE; - } - - return PL_DHASH_NEXT; - } - }; - - mFileActors.Enumerate(&Helper::Remove, aFileActor); -} - -void -IDBDatabase::NoteReceivedBlob(nsIDOMBlob* aBlob) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aBlob); - MOZ_ASSERT(mBackgroundActor); - -#ifdef DEBUG - { - nsRefPtr blobImpl = static_cast(aBlob)->Impl(); - MOZ_ASSERT(blobImpl); - - nsCOMPtr remoteBlob = do_QueryObject(blobImpl); - MOZ_ASSERT(remoteBlob); - - BlobChild* blobChild = remoteBlob->GetBlobChild(); - MOZ_ASSERT(blobChild); - - PBackgroundChild* backgroundManager = blobChild->GetBackgroundManager(); - MOZ_ASSERT(backgroundManager); - - PBackgroundChild* thisManager = mBackgroundActor->Manager()->Manager(); - MOZ_ASSERT(thisManager); - - MOZ_ASSERT(thisManager == backgroundManager); - } -#endif - - nsCOMPtr weakRef = do_GetWeakReference(aBlob); - MOZ_ASSERT(weakRef); - - // It's ok if this entry already exists in the table. - mReceivedBlobs.PutEntry(weakRef); -} - -void -IDBDatabase::DelayedMaybeExpireFileActors() -{ - AssertIsOnOwningThread(); - - if (!mBackgroundActor || !mFileActors.Count()) { - return; - } - - nsCOMPtr runnable = - NS_NewRunnableMethodWithArg(this, - &IDBDatabase::ExpireFileActors, - /* aExpireAll */ false); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable))); -} - -nsresult -IDBDatabase::GetQuotaInfo(nsACString& aOrigin, - PersistenceType* aPersistenceType) -{ - using mozilla::dom::quota::QuotaManager; - - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - if (aPersistenceType) { - *aPersistenceType = mSpec->metadata().persistenceType(); - MOZ_ASSERT(*aPersistenceType != PERSISTENCE_TYPE_INVALID); - } - - PrincipalInfo* principalInfo = mFactory->GetPrincipalInfo(); - MOZ_ASSERT(principalInfo); - - switch (principalInfo->type()) { - case PrincipalInfo::TNullPrincipalInfo: - MOZ_CRASH("Is this needed?!"); - - case PrincipalInfo::TSystemPrincipalInfo: - QuotaManager::GetInfoForChrome(nullptr, &aOrigin, nullptr, nullptr); - return NS_OK; - - case PrincipalInfo::TContentPrincipalInfo: { - nsresult rv; - nsCOMPtr principal = - PrincipalInfoToPrincipal(*principalInfo, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = QuotaManager::GetInfoFromPrincipal(principal, - nullptr, - &aOrigin, - nullptr, - nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; - } - - default: - MOZ_CRASH("Unknown PrincipalInfo type!"); - } - - MOZ_CRASH("Should never get here!"); -} - -void -IDBDatabase::ExpireFileActors(bool aExpireAll) -{ - AssertIsOnOwningThread(); - - class MOZ_STACK_CLASS Helper MOZ_FINAL - { - public: - static PLDHashOperator - MaybeExpireFileActors(nsISupports* aKey, - PBackgroundIDBDatabaseFileChild*& aValue, - void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(aValue); - MOZ_ASSERT(aClosure); - - const bool expiringAll = *static_cast(aClosure); - - bool shouldExpire = expiringAll; - if (!shouldExpire) { - nsCOMPtr weakRef = do_QueryInterface(aKey); - MOZ_ASSERT(weakRef); - - nsCOMPtr referent = do_QueryReferent(weakRef); - shouldExpire = !referent; - } - - if (shouldExpire) { - PBackgroundIDBDatabaseFileChild::Send__delete__(aValue); - - if (!expiringAll) { - return PL_DHASH_REMOVE; - } - } - - return PL_DHASH_NEXT; - } - - static PLDHashOperator - MaybeExpireReceivedBlobs(nsISupportsHashKey* aKey, - void* aClosure) - { - MOZ_ASSERT(aKey); - MOZ_ASSERT(!aClosure); - - nsISupports* key = aKey->GetKey(); - MOZ_ASSERT(key); - - nsCOMPtr weakRef = do_QueryInterface(key); - MOZ_ASSERT(weakRef); - - nsCOMPtr referent = do_QueryReferent(weakRef); - if (!referent) { - return PL_DHASH_REMOVE; - } - - return PL_DHASH_NEXT; - } - }; - - if (mBackgroundActor && mFileActors.Count()) { - mFileActors.Enumerate(&Helper::MaybeExpireFileActors, &aExpireAll); - if (aExpireAll) { - mFileActors.Clear(); - } - } else { - MOZ_ASSERT(!mFileActors.Count()); - } - - if (mReceivedBlobs.Count()) { - if (aExpireAll) { - mReceivedBlobs.Clear(); - } else { - mReceivedBlobs.EnumerateEntries(&Helper::MaybeExpireReceivedBlobs, - nullptr); - } - } -} - -void -IDBDatabase::NoteLiveMutableFile(IDBMutableFile* aMutableFile) -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aMutableFile); - MOZ_ASSERT(!mLiveMutableFiles.Contains(aMutableFile)); - - mLiveMutableFiles.AppendElement(aMutableFile); -} - -void -IDBDatabase::NoteFinishedMutableFile(IDBMutableFile* aMutableFile) -{ - // This should always happen in the main process but occasionally it is called - // after the IndexedDatabaseManager has already shut down. - // MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aMutableFile); - - // It's ok if this is called more than once, so don't assert that aMutableFile - // is in the list already. - - mLiveMutableFiles.RemoveElement(aMutableFile); -} - -void -IDBDatabase::InvalidateMutableFiles() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mLiveMutableFiles.IsEmpty()) { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - for (uint32_t count = mLiveMutableFiles.Length(), index = 0; - index < count; - index++) { - mLiveMutableFiles[index]->Invalidate(); - } - - mLiveMutableFiles.Clear(); - } -} - -void -IDBDatabase::Invalidate() -{ - AssertIsOnOwningThread(); - - if (!mInvalidated) { - mInvalidated = true; - - InvalidateInternal(); - } -} - -NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) - -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) - tmp->AssertIsOnOwningThread(); - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) - tmp->AssertIsOnOwningThread(); - - // Don't unlink mFactory! - - // We've been unlinked, at the very least we should be able to prevent further - // transactions from starting and unblock any other SetVersion callers. - tmp->CloseInternal(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -void -IDBDatabase::LastRelease() -{ - AssertIsOnOwningThread(); - - CloseInternal(); - - if (mBackgroundActor) { - mBackgroundActor->SendDeleteMeInternal(); - MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); - } + return mASCIIOrigin; } nsresult @@ -1193,239 +768,171 @@ IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor) return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); } -JSObject* -IDBDatabase::WrapObject(JSContext* aCx) +AsyncConnectionHelper::ChildProcessSendResult +NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) { - return IDBDatabaseBinding::Wrap(aCx, this); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + return Success_NotSent; } -CreateFileHelper::CreateFileHelper(IDBDatabase* aDatabase, - IDBRequest* aRequest, - const nsAString& aName, - const nsAString& aType, - const nsACString& aOrigin) - : mDatabase(aDatabase) - , mRequest(aRequest) - , mName(aName) - , mType(aType) - , mDatabaseName(aDatabase->Name()) - , mOrigin(aOrigin) - , mPersistenceType(aDatabase->Spec()->metadata().persistenceType()) - , mResultCode(NS_OK) -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDatabase); - MOZ_ASSERT(aRequest); - MOZ_ASSERT(mPersistenceType != PERSISTENCE_TYPE_INVALID); -} - -// static nsresult -CreateFileHelper::CreateAndDispatch(IDBDatabase* aDatabase, - IDBRequest* aRequest, - const nsAString& aName, - const nsAString& aType) +NoRequestDatabaseHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); - MOZ_ASSERT(aDatabase->Factory()); - MOZ_ASSERT(aRequest); - MOZ_ASSERT(!QuotaManager::IsShuttingDown()); + MOZ_CRASH("Should never get here!"); +} - nsCString origin; - nsresult rv = aDatabase->GetQuotaInfo(origin, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; +nsresult +NoRequestDatabaseHelper::OnSuccess() +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + return NS_OK; +} + +void +NoRequestDatabaseHelper::OnError() +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mTransaction->Abort(GetResultCode()); +} + +nsresult +CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("CreateObjectStoreHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { + NS_WARNING("Refusing to create additional objectStore because disk space " + "is low!"); + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; } - MOZ_ASSERT(!origin.IsEmpty()); + nsCOMPtr stmt = + mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( + "INSERT INTO object_store (id, auto_increment, name, key_path) " + "VALUES (:id, :auto_increment, :name, :key_path)" + )); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsRefPtr helper = - new CreateFileHelper(aDatabase, aRequest, aName, aType, origin); + mozStorageStatementScoper scoper(stmt); - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsCOMPtr ioThread = quotaManager->IOThread(); - MOZ_ASSERT(ioThread); + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"), + mObjectStore->IsAutoIncrement() ? 1 : 0); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - if (NS_WARN_IF(NS_FAILED(ioThread->Dispatch(helper, NS_DISPATCH_NORMAL)))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + const KeyPath& keyPath = mObjectStore->GetKeyPath(); + if (keyPath.IsValid()) { + nsAutoString keyPathSerialization; + keyPath.SerializeToString(keyPathSerialization); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), + keyPathSerialization); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } + else { + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path")); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + rv = stmt->Execute(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +void +CreateObjectStoreHelper::ReleaseMainThreadObjects() +{ + mObjectStore = nullptr; + NoRequestDatabaseHelper::ReleaseMainThreadObjects(); +} + +nsresult +DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("DeleteObjectStoreHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( + "DELETE FROM object_store " + "WHERE id = :id " + )); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->Execute(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return NS_OK; } nsresult -CreateFileHelper::DoDatabaseWork() +CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection) { AssertIsOnIOThread(); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(!mFileInfo); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - PROFILER_LABEL("IndexedDB", - "CreateFileHelper::DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); + PROFILER_LABEL("CreateFileHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); if (IndexedDatabaseManager::InLowDiskSpaceMode()) { NS_WARNING("Refusing to create file because disk space is low!"); return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; } - IndexedDatabaseManager* idbManager = IndexedDatabaseManager::Get(); - MOZ_ASSERT(idbManager); + FileManager* fileManager = mDatabase->Manager(); - nsRefPtr fileManager = - idbManager->GetFileManager(mPersistenceType, mOrigin, mDatabaseName); - MOZ_ASSERT(fileManager); + mFileInfo = fileManager->GetNewFileInfo(); + IDB_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsRefPtr fileInfo = fileManager->GetNewFileInfo(); - if (NS_WARN_IF(!fileInfo)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + const int64_t& fileId = mFileInfo->Id(); - const int64_t fileId = fileInfo->Id(); + nsCOMPtr directory = fileManager->EnsureJournalDirectory(); + NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); - nsCOMPtr journalDirectory = fileManager->EnsureJournalDirectory(); - if (NS_WARN_IF(!journalDirectory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + nsCOMPtr file = fileManager->GetFileForId(directory, fileId); + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); - nsCOMPtr journalFile = - fileManager->GetFileForId(journalDirectory, fileId); - if (NS_WARN_IF(!journalFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + NS_ENSURE_SUCCESS(rv, rv); - nsresult rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + directory = fileManager->GetDirectory(); + IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsCOMPtr fileDirectory = fileManager->GetDirectory(); - if (NS_WARN_IF(!fileDirectory)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr file = fileManager->GetFileForId(fileDirectory, fileId); - if (NS_WARN_IF(!file)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + file = fileManager->GetFileForId(directory, fileId); + IDB_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mFileInfo.swap(fileInfo); - return NS_OK; -} - -void -CreateFileHelper::DoMainThreadWork(nsresult aResultCode) -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - if (mDatabase->IsInvalidated()) { - IDB_REPORT_INTERNAL_ERR(); - aResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsRefPtr mutableFile; - if (NS_SUCCEEDED(aResultCode)) { - mutableFile = - IDBMutableFile::Create(mDatabase, mName, mType, mFileInfo.forget()); - MOZ_ASSERT(mutableFile); - } - - DispatchMutableFileResult(mRequest, aResultCode, mutableFile); -} - -NS_IMPL_ISUPPORTS_INHERITED0(CreateFileHelper, nsRunnable) - -NS_IMETHODIMP -CreateFileHelper::Run() -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - if (NS_IsMainThread()) { - DoMainThreadWork(mResultCode); - - mDatabase = nullptr; - mRequest = nullptr; - mFileInfo = nullptr; - - return NS_OK; - } - - AssertIsOnIOThread(); - MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); - - nsresult rv = DoDatabaseWork(); - if (NS_WARN_IF(NS_FAILED(rv))) { - mResultCode = rv; - } - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return NS_OK; } -NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver) - -NS_IMETHODIMP -IDBDatabase:: -Observer::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) +nsresult +CreateFileHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aTopic); + nsRefPtr mutableFile = + IDBMutableFile::Create(mName, mType, mDatabase, mFileInfo.forget()); + IDB_ENSURE_TRUE(mutableFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - if (!strcmp(aTopic, kWindowObserverTopic)) { - if (mWeakDatabase) { - nsCOMPtr supportsInt = do_QueryInterface(aSubject); - MOZ_ASSERT(supportsInt); - - uint64_t windowId; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(supportsInt->GetData(&windowId))); - - if (windowId == mWindowId) { - nsRefPtr database = mWeakDatabase; - mWeakDatabase = nullptr; - - database->InvalidateInternal(); - } - } - - return NS_OK; - } - - if (!strcmp(aTopic, kCycleCollectionObserverTopic) || - !strcmp(aTopic, kMemoryPressureObserverTopic)) { - if (mWeakDatabase) { - nsRefPtr database = mWeakDatabase; - - database->ExpireFileActors(/* aExpireAll */ false); - } - - return NS_OK; - } - - NS_WARNING("Unknown observer topic!"); - return NS_OK; + return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mutableFile), aVal); } - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index ab0a041475b9..91d8681ed34c 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -7,302 +7,274 @@ #ifndef mozilla_dom_indexeddb_idbdatabase_h__ #define mozilla_dom_indexeddb_idbdatabase_h__ -#include "mozilla/Attributes.h" -#include "mozilla/dom/IDBTransactionBinding.h" -#include "mozilla/dom/StorageTypeBinding.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" -#include "mozilla/dom/quota/PersistenceType.h" -#include "nsAutoPtr.h" -#include "nsDataHashtable.h" -#include "nsHashKeys.h" -#include "nsString.h" -#include "nsTHashtable.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" -class nsIDocument; -class nsIDOMBlob; -class nsIWeakReference; +#include "nsIDocument.h" +#include "nsIOfflineStorage.h" + +#include "mozilla/Attributes.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/IDBObjectStoreBinding.h" +#include "mozilla/dom/IDBTransactionBinding.h" +#include "mozilla/dom/quota/PersistenceType.h" + +#include "mozilla/dom/indexedDB/FileManager.h" +#include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" + +class nsIScriptContext; class nsPIDOMWindow; namespace mozilla { - -class ErrorResult; class EventChainPostVisitor; - namespace dom { +class nsIContentParent; +namespace quota { +class Client; +} +} +} -class DOMStringList; -struct IDBObjectStoreParameters; -template class Sequence; +BEGIN_INDEXEDDB_NAMESPACE -namespace indexedDB { - -class BackgroundDatabaseChild; -class DatabaseSpec; -class FileManager; +class AsyncConnectionHelper; +struct DatabaseInfo; class IDBFactory; -class IDBMutableFile; +class IDBIndex; class IDBObjectStore; -class IDBRequest; class IDBTransaction; -class PBackgroundIDBDatabaseFileChild; +class IndexedDatabaseManager; +class IndexedDBDatabaseChild; +class IndexedDBDatabaseParent; +struct ObjectStoreInfoGuts; -class IDBDatabase MOZ_FINAL - : public IDBWrapperCache +class IDBDatabase MOZ_FINAL : public IDBWrapperCache, + public nsIOfflineStorage { - typedef mozilla::dom::StorageType StorageType; - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - class Observer; - friend class Observer; - - // The factory must be kept alive when IndexedDB is used in multiple - // processes. If it dies then the entire actor tree will be destroyed with it - // and the world will explode. - nsRefPtr mFactory; - - nsAutoPtr mSpec; - - // Normally null except during a versionchange transaction. - nsAutoPtr mPreviousSpec; - - nsRefPtr mFileManager; - - BackgroundDatabaseChild* mBackgroundActor; - - nsTHashtable> mTransactions; - - nsDataHashtable - mFileActors; - - nsTHashtable mReceivedBlobs; - - nsRefPtr mObserver; - - // Weak refs, IDBMutableFile strongly owns this IDBDatabase object. - nsTArray mLiveMutableFiles; - - bool mClosed; - bool mInvalidated; + friend class AsyncConnectionHelper; + friend class IndexedDatabaseManager; + friend class IndexedDBDatabaseParent; + friend class IndexedDBDatabaseChild; public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOFFLINESTORAGE + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache) + static already_AddRefed Create(IDBWrapperCache* aOwnerCache, IDBFactory* aFactory, - BackgroundDatabaseChild* aActor, - DatabaseSpec* aSpec); + already_AddRefed aDatabaseInfo, + const nsACString& aASCIIOrigin, + FileManager* aFileManager, + mozilla::dom::nsIContentParent* aContentParent); - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif + static IDBDatabase* + FromStorage(nsIOfflineStorage* aStorage); - const nsString& - Name() const; + // nsIDOMEventTarget + virtual nsresult PostHandleEvent( + EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; - void - GetName(nsAString& aName) const + DatabaseInfo* Info() const { - AssertIsOnOwningThread(); + return mDatabaseInfo; + } - aName = Name(); + const nsString& Name() const + { + return mName; + } + + const nsString& FilePath() const + { + return mFilePath; + } + + already_AddRefed GetOwnerDocument() + { + if (!GetOwner()) { + return nullptr; + } + + nsCOMPtr doc = GetOwner()->GetExtantDoc(); + return doc.forget(); + } + + // Whether or not the database has been invalidated. If it has then no further + // transactions for this database will be allowed to run. This function may be + // called on any thread. + bool IsInvalidated() const + { + return mInvalidated; + } + + void DisconnectFromActorParent(); + + void CloseInternal(bool aIsDead); + + void EnterSetVersionTransaction(); + void ExitSetVersionTransaction(); + + // Called when a versionchange transaction is aborted to reset the + // DatabaseInfo. + void RevertToPreviousState(); + + FileManager* Manager() const + { + return mFileManager; + } + + void + SetActor(IndexedDBDatabaseChild* aActorChild) + { + NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); + mActorChild = aActorChild; + } + + void + SetActor(IndexedDBDatabaseParent* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, + "Shouldn't have more than one!"); + mActorParent = aActorParent; + } + + IndexedDBDatabaseChild* + GetActorChild() const + { + return mActorChild; + } + + IndexedDBDatabaseParent* + GetActorParent() const + { + return mActorParent; + } + + mozilla::dom::nsIContentParent* + GetContentParent() const + { + return mContentParent; + } + + already_AddRefed + CreateObjectStoreInternal(IDBTransaction* aTransaction, + const ObjectStoreInfoGuts& aInfo, + ErrorResult& aRv); + + IDBFactory* + Factory() const + { + return mFactory; + } + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // WebIDL + nsPIDOMWindow* + GetParentObject() const + { + return GetOwner(); + } + + void + GetName(nsString& aName) const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + aName.Assign(mName); } uint64_t Version() const; - already_AddRefed - GetOwnerDocument() const; - - void - Close() - { - AssertIsOnOwningThread(); - - CloseInternal(); - } - - bool - IsClosed() const - { - AssertIsOnOwningThread(); - - return mClosed; - } - - void - Invalidate(); - - // Whether or not the database has been invalidated. If it has then no further - // transactions for this database will be allowed to run. - bool - IsInvalidated() const - { - AssertIsOnOwningThread(); - - return mInvalidated; - } - - void - EnterSetVersionTransaction(uint64_t aNewVersion); - - void - ExitSetVersionTransaction(); - - // Called when a versionchange transaction is aborted to reset the - // DatabaseInfo. - void - RevertToPreviousState(); - - IDBFactory* - Factory() const - { - AssertIsOnOwningThread(); - - return mFactory; - } - - void - RegisterTransaction(IDBTransaction* aTransaction); - - void - UnregisterTransaction(IDBTransaction* aTransaction); - - void - AbortTransactions(); - - PBackgroundIDBDatabaseFileChild* - GetOrCreateFileActorForBlob(nsIDOMBlob* aBlob); - - void - NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor); - - void - NoteReceivedBlob(nsIDOMBlob* aBlob); - - void - DelayedMaybeExpireFileActors(); - - // XXX This doesn't really belong here... It's only needed for IDBMutableFile - // serialization and should be removed someday. - nsresult - GetQuotaInfo(nsACString& aOrigin, PersistenceType* aPersistenceType); - - void - NoteLiveMutableFile(IDBMutableFile* aMutableFile); - - void - NoteFinishedMutableFile(IDBMutableFile* aMutableFile); - - nsPIDOMWindow* - GetParentObject() const; - - already_AddRefed - ObjectStoreNames() const; + already_AddRefed + GetObjectStoreNames(ErrorResult& aRv) const; already_AddRefed - CreateObjectStore(JSContext* aCx, - const nsAString& aName, + CreateObjectStore(JSContext* aCx, const nsAString& aName, const IDBObjectStoreParameters& aOptionalParameters, ErrorResult& aRv); void DeleteObjectStore(const nsAString& name, ErrorResult& aRv); - already_AddRefed - Transaction(const nsAString& aStoreName, - IDBTransactionMode aMode, - ErrorResult& aRv); + already_AddRefed + Transaction(const nsAString& aStoreName, IDBTransactionMode aMode, + ErrorResult& aRv) + { + Sequence list; + list.AppendElement(aStoreName); + return Transaction(list, aMode, aRv); + } - already_AddRefed - Transaction(const Sequence& aStoreNames, - IDBTransactionMode aMode, + already_AddRefed + Transaction(const Sequence& aStoreNames, IDBTransactionMode aMode, ErrorResult& aRv); - StorageType - Storage() const; - IMPL_EVENT_HANDLER(abort) IMPL_EVENT_HANDLER(error) IMPL_EVENT_HANDLER(versionchange) + mozilla::dom::StorageType + Storage() const + { + return PersistenceTypeToStorage(mPersistenceType); + } + already_AddRefed - CreateMutableFile(const nsAString& aName, - const Optional& aType, + CreateMutableFile(const nsAString& aName, const Optional& aType, ErrorResult& aRv); already_AddRefed - MozCreateFileHandle(const nsAString& aName, - const Optional& aType, + MozCreateFileHandle(const nsAString& aName, const Optional& aType, ErrorResult& aRv) { return CreateMutableFile(aName, aType, aRv); } - void - ClearBackgroundActor() - { - AssertIsOnOwningThread(); - - mBackgroundActor = nullptr; - } - - const DatabaseSpec* - Spec() const - { - return mSpec; - } - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache) - - // nsIDOMEventTarget - virtual void - LastRelease() MOZ_OVERRIDE; - - virtual nsresult - PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; + virtual void LastRelease() MOZ_OVERRIDE; private: - IDBDatabase(IDBWrapperCache* aOwnerCache, - IDBFactory* aFactory, - BackgroundDatabaseChild* aActor, - DatabaseSpec* aSpec); - + explicit IDBDatabase(IDBWrapperCache* aOwnerCache); ~IDBDatabase(); - void - CloseInternal(); + void OnUnlink(); + void InvalidateInternal(bool aIsDead); - void - InvalidateInternal(); + // The factory must be kept alive when IndexedDB is used in multiple + // processes. If it dies then the entire actor tree will be destroyed with it + // and the world will explode. + nsRefPtr mFactory; - bool - RunningVersionChangeTransaction() const - { - AssertIsOnOwningThread(); + nsRefPtr mDatabaseInfo; - return !!mPreviousSpec; - } + // Set to a copy of the existing DatabaseInfo when starting a versionchange + // transaction. + nsRefPtr mPreviousDatabaseInfo; + nsCString mDatabaseId; + nsString mName; + nsString mFilePath; + nsCString mASCIIOrigin; - void - RefreshSpec(bool aMayDelete); + nsRefPtr mFileManager; - void - ExpireFileActors(bool aExpireAll); + IndexedDBDatabaseChild* mActorChild; + IndexedDBDatabaseParent* mActorParent; - void - InvalidateMutableFiles(); + mozilla::dom::nsIContentParent* mContentParent; + + nsRefPtr mQuotaClient; + + bool mInvalidated; + bool mRegistered; + bool mClosed; + bool mRunningVersionChange; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbdatabase_h__ diff --git a/dom/indexedDB/IDBEvents.cpp b/dom/indexedDB/IDBEvents.cpp index cd9ce79cf1d5..a4c58bee3a62 100644 --- a/dom/indexedDB/IDBEvents.cpp +++ b/dom/indexedDB/IDBEvents.cpp @@ -6,45 +6,49 @@ #include "IDBEvents.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/EventTarget.h" -#include "mozilla/dom/IDBVersionChangeEventBinding.h" -#include "nsString.h" +#include "nsJSON.h" +#include "nsThreadUtils.h" -using namespace mozilla; +#include "IDBRequest.h" +#include "IDBTransaction.h" + +USING_INDEXEDDB_NAMESPACE using namespace mozilla::dom; -using namespace mozilla::dom::indexedDB; -namespace mozilla { -namespace dom { -namespace indexedDB { +namespace { -const char16_t* kAbortEventType = MOZ_UTF16("abort"); -const char16_t* kBlockedEventType = MOZ_UTF16("blocked"); -const char16_t* kCompleteEventType = MOZ_UTF16("complete"); -const char16_t* kErrorEventType = MOZ_UTF16("error"); -const char16_t* kSuccessEventType = MOZ_UTF16("success"); -const char16_t* kUpgradeNeededEventType = MOZ_UTF16("upgradeneeded"); -const char16_t* kVersionChangeEventType = MOZ_UTF16("versionchange"); +class EventFiringRunnable : public nsRunnable +{ +public: + EventFiringRunnable(EventTarget* aTarget, + nsIDOMEvent* aEvent) + : mTarget(aTarget), mEvent(aEvent) + { } + + NS_IMETHOD Run() { + bool dummy; + return mTarget->DispatchEvent(mEvent, &dummy); + } + +private: + nsCOMPtr mTarget; + nsCOMPtr mEvent; +}; + +} // anonymous namespace already_AddRefed -CreateGenericEvent(EventTarget* aOwner, - const nsDependentString& aType, - Bubbles aBubbles, - Cancelable aCancelable) +mozilla::dom::indexedDB::CreateGenericEvent(mozilla::dom::EventTarget* aOwner, + const nsAString& aType, + Bubbles aBubbles, + Cancelable aCancelable) { nsCOMPtr event; - nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), aOwner, nullptr, nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - rv = event->InitEvent(aType, - aBubbles == eDoesBubble ? true : false, - aCancelable == eCancelable ? true : false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } + NS_NewDOMEvent(getter_AddRefs(event), aOwner, nullptr, nullptr); + nsresult rv = event->InitEvent(aType, + aBubbles == eDoesBubble ? true : false, + aCancelable == eCancelable ? true : false); + NS_ENSURE_SUCCESS(rv, nullptr); event->SetTrusted(true); @@ -53,39 +57,37 @@ CreateGenericEvent(EventTarget* aOwner, // static already_AddRefed -IDBVersionChangeEvent::CreateInternal(EventTarget* aOwner, +IDBVersionChangeEvent::CreateInternal(mozilla::dom::EventTarget* aOwner, const nsAString& aType, uint64_t aOldVersion, - Nullable aNewVersion) + uint64_t aNewVersion) { - nsRefPtr event = - new IDBVersionChangeEvent(aOwner, aOldVersion); - if (!aNewVersion.IsNull()) { - event->mNewVersion.SetValue(aNewVersion.Value()); - } + nsRefPtr event(new IDBVersionChangeEvent(aOwner)); nsresult rv = event->InitEvent(aType, false, false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } + NS_ENSURE_SUCCESS(rv, nullptr); event->SetTrusted(true); + event->mOldVersion = aOldVersion; + event->mNewVersion = aNewVersion; + return event.forget(); } -already_AddRefed -IDBVersionChangeEvent::Constructor(const GlobalObject& aGlobal, - const nsAString& aType, - const IDBVersionChangeEventInit& aOptions, - ErrorResult& aRv) +// static +already_AddRefed +IDBVersionChangeEvent::CreateRunnableInternal(mozilla::dom::EventTarget* aTarget, + const nsAString& aType, + uint64_t aOldVersion, + uint64_t aNewVersion) { - nsCOMPtr target = do_QueryInterface(aGlobal.GetAsSupports()); + nsRefPtr event = + CreateInternal(aTarget, aType, aOldVersion, aNewVersion); + NS_ENSURE_TRUE(event, nullptr); - return CreateInternal(target, - aType, - aOptions.mOldVersion, - aOptions.mNewVersion); + nsCOMPtr runnable(new EventFiringRunnable(aTarget, event)); + return runnable.forget(); } NS_IMPL_ADDREF_INHERITED(IDBVersionChangeEvent, Event) @@ -94,13 +96,3 @@ NS_IMPL_RELEASE_INHERITED(IDBVersionChangeEvent, Event) NS_INTERFACE_MAP_BEGIN(IDBVersionChangeEvent) NS_INTERFACE_MAP_ENTRY(IDBVersionChangeEvent) NS_INTERFACE_MAP_END_INHERITING(Event) - -JSObject* -IDBVersionChangeEvent::WrapObject(JSContext* aCx) -{ - return IDBVersionChangeEventBinding::Wrap(aCx, this); -} - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/IDBEvents.h b/dom/indexedDB/IDBEvents.h index c4844c7ee7e0..af942d286d79 100644 --- a/dom/indexedDB/IDBEvents.h +++ b/dom/indexedDB/IDBEvents.h @@ -7,28 +7,28 @@ #ifndef mozilla_dom_indexeddb_idbevents_h__ #define mozilla_dom_indexeddb_idbevents_h__ -#include "js/RootingAPI.h" -#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + +#include "nsIRunnable.h" + #include "mozilla/dom/Event.h" #include "mozilla/dom/Nullable.h" +#include "mozilla/dom/indexedDB/IDBObjectStore.h" +#include "mozilla/dom/IDBVersionChangeEventBinding.h" + +#define SUCCESS_EVT_STR "success" +#define ERROR_EVT_STR "error" +#define COMPLETE_EVT_STR "complete" +#define ABORT_EVT_STR "abort" +#define VERSIONCHANGE_EVT_STR "versionchange" +#define BLOCKED_EVT_STR "blocked" +#define UPGRADENEEDED_EVT_STR "upgradeneeded" #define IDBVERSIONCHANGEEVENT_IID \ - {0x3b65d4c3, 0x73ad, 0x492e, {0xb1, 0x2d, 0x15, 0xf9, 0xda, 0xc2, 0x08, 0x4b}} + { 0x3b65d4c3, 0x73ad, 0x492e, \ + { 0xb1, 0x2d, 0x15, 0xf9, 0xda, 0xc2, 0x08, 0x4b } } -class nsAString; -class nsDependentString; - -namespace mozilla { - -class ErrorResult; - -namespace dom { - -class EventTarget; -class GlobalObject; -struct IDBVersionChangeEventInit; - -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE enum Bubbles { eDoesNotBubble, @@ -40,94 +40,125 @@ enum Cancelable { eCancelable }; -extern const char16_t* kAbortEventType; -extern const char16_t* kBlockedEventType; -extern const char16_t* kCompleteEventType; -extern const char16_t* kErrorEventType; -extern const char16_t* kSuccessEventType; -extern const char16_t* kUpgradeNeededEventType; -extern const char16_t* kVersionChangeEventType; - already_AddRefed -CreateGenericEvent(EventTarget* aOwner, - const nsDependentString& aType, +CreateGenericEvent(mozilla::dom::EventTarget* aOwner, + const nsAString& aType, Bubbles aBubbles, Cancelable aCancelable); -class IDBVersionChangeEvent MOZ_FINAL : public Event +class IDBVersionChangeEvent : public Event { - uint64_t mOldVersion; - Nullable mNewVersion; - public: - static already_AddRefed - Create(EventTarget* aOwner, - const nsDependentString& aName, - uint64_t aOldVersion, - uint64_t aNewVersion) - { - Nullable newVersion(aNewVersion); - return CreateInternal(aOwner, aName, aOldVersion, newVersion); - } + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_TO_EVENT + NS_DECLARE_STATIC_IID_ACCESSOR(IDBVERSIONCHANGEEVENT_IID) - static already_AddRefed - Create(EventTarget* aOwner, - const nsDependentString& aName, - uint64_t aOldVersion) + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE { - Nullable newVersion(0); - newVersion.SetNull(); - return CreateInternal(aOwner, aName, aOldVersion, newVersion); + return mozilla::dom::IDBVersionChangeEventBinding::Wrap(aCx, this); } static already_AddRefed Constructor(const GlobalObject& aGlobal, const nsAString& aType, const IDBVersionChangeEventInit& aOptions, - ErrorResult& aRv); + ErrorResult& aRv) + { + uint64_t newVersion = 0; + if (!aOptions.mNewVersion.IsNull()) { + newVersion = aOptions.mNewVersion.Value(); + } + nsCOMPtr target = do_QueryInterface(aGlobal.GetAsSupports()); + return CreateInternal(target, aType, aOptions.mOldVersion, newVersion); + } - uint64_t - OldVersion() const + uint64_t OldVersion() { return mOldVersion; } - Nullable - GetNewVersion() const + mozilla::dom::Nullable GetNewVersion() { - return mNewVersion; + return mNewVersion + ? mozilla::dom::Nullable(mNewVersion) + : mozilla::dom::Nullable(); } - NS_DECLARE_STATIC_IID_ACCESSOR(IDBVERSIONCHANGEEVENT_IID) + inline static already_AddRefed + Create(mozilla::dom::EventTarget* aOwner, + int64_t aOldVersion, + int64_t aNewVersion) + { + return CreateInternal(aOwner, + NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), + aOldVersion, aNewVersion); + } - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_TO_EVENT + inline static already_AddRefed + CreateBlocked(mozilla::dom::EventTarget* aOwner, + uint64_t aOldVersion, + uint64_t aNewVersion) + { + return CreateInternal(aOwner, NS_LITERAL_STRING(BLOCKED_EVT_STR), + aOldVersion, aNewVersion); + } - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; + inline static already_AddRefed + CreateUpgradeNeeded(mozilla::dom::EventTarget* aOwner, + uint64_t aOldVersion, + uint64_t aNewVersion) + { + return CreateInternal(aOwner, + NS_LITERAL_STRING(UPGRADENEEDED_EVT_STR), + aOldVersion, aNewVersion); + } -private: - IDBVersionChangeEvent(EventTarget* aOwner, uint64_t aOldVersion) + inline static already_AddRefed + CreateRunnable(mozilla::dom::EventTarget* aTarget, + uint64_t aOldVersion, + uint64_t aNewVersion) + { + return CreateRunnableInternal(aTarget, + NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), + aOldVersion, aNewVersion); + } + + static already_AddRefed + CreateBlockedRunnable(mozilla::dom::EventTarget* aTarget, + uint64_t aOldVersion, + uint64_t aNewVersion) + { + return CreateRunnableInternal(aTarget, + NS_LITERAL_STRING(BLOCKED_EVT_STR), + aOldVersion, aNewVersion); + } + +protected: + explicit IDBVersionChangeEvent(mozilla::dom::EventTarget* aOwner) : Event(aOwner, nullptr, nullptr) - , mOldVersion(aOldVersion) { SetIsDOMBinding(); } - - ~IDBVersionChangeEvent() - { } + virtual ~IDBVersionChangeEvent() { } static already_AddRefed - CreateInternal(EventTarget* aOwner, - const nsAString& aName, + CreateInternal(mozilla::dom::EventTarget* aOwner, + const nsAString& aType, uint64_t aOldVersion, - Nullable aNewVersion); + uint64_t aNewVersion); + + static already_AddRefed + CreateRunnableInternal(mozilla::dom::EventTarget* aOwner, + const nsAString& aType, + uint64_t aOldVersion, + uint64_t aNewVersion); + + uint64_t mOldVersion; + uint64_t mNewVersion; }; NS_DEFINE_STATIC_IID_ACCESSOR(IDBVersionChangeEvent, IDBVERSIONCHANGEEVENT_IID) -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbevents_h__ diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index e0681af60de7..a17ce5d1f865 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -6,189 +6,169 @@ #include "IDBFactory.h" -#include "IDBRequest.h" -#include "IndexedDatabaseManager.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/Preferences.h" -#include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/dom/IDBFactoryBinding.h" -#include "mozilla/dom/TabChild.h" -#include "mozilla/ipc/BackgroundChild.h" -#include "mozilla/ipc/BackgroundUtils.h" -#include "mozilla/ipc/PBackground.h" -#include "mozilla/ipc/PBackgroundChild.h" -#include "nsGlobalWindow.h" -#include "nsIIPCBackgroundChildCreateCallback.h" -#include "nsILoadContext.h" +#include "nsIFile.h" #include "nsIPrincipal.h" -#include "nsIWebNavigation.h" +#include "nsIScriptContext.h" +#include "nsIScriptSecurityManager.h" +#include "nsIXPConnect.h" +#include "nsIXPCScriptable.h" + +#include +#include "mozilla/dom/nsIContentParent.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/IDBFactoryBinding.h" +#include "mozilla/dom/PBrowserChild.h" +#include "mozilla/dom/quota/OriginOrPatternString.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/Preferences.h" +#include "mozilla/storage.h" +#include "nsComponentManagerUtils.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsContentUtils.h" +#include "nsDOMClassInfoID.h" +#include "nsGlobalWindow.h" +#include "nsHashKeys.h" +#include "nsPIDOMWindow.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "nsXPCOMCID.h" + +#include "AsyncConnectionHelper.h" +#include "CheckPermissionsHelper.h" +#include "DatabaseInfo.h" +#include "IDBDatabase.h" +#include "IDBEvents.h" +#include "IDBKeyRange.h" +#include "IndexedDatabaseManager.h" +#include "Key.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" +#include "nsNetUtil.h" -// Include this last to avoid path problems on Windows. -#include "ActorsChild.h" +#include "ipc/IndexedDBChild.h" -#ifdef DEBUG -#include "nsContentUtils.h" // For IsCallerChrome assertions. -#endif +#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled" -namespace mozilla { -namespace dom { -namespace indexedDB { +USING_INDEXEDDB_NAMESPACE +USING_QUOTA_NAMESPACE -using namespace mozilla::dom::quota; -using namespace mozilla::ipc; +using mozilla::dom::ContentChild; +using mozilla::dom::nsIContentParent; +using mozilla::dom::IDBOpenDBOptions; +using mozilla::dom::NonNull; +using mozilla::dom::Optional; +using mozilla::dom::TabChild; +using mozilla::ErrorResult; +using mozilla::Preferences; namespace { -const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled"; - -nsresult -GetPrincipalInfoFromPrincipal(nsIPrincipal* aPrincipal, - PrincipalInfo* aPrincipalInfo) +struct ObjectStoreInfoMap { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aPrincipalInfo); + ObjectStoreInfoMap() + : id(INT64_MIN), info(nullptr) { } - bool isNullPrincipal; - nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (isNullPrincipal) { - NS_WARNING("IndexedDB not supported from this principal!"); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - rv = PrincipalToPrincipalInfo(aPrincipal, aPrincipalInfo); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; -} + int64_t id; + ObjectStoreInfo* info; +}; } // anonymous namespace -class IDBFactory::BackgroundCreateCallback MOZ_FINAL - : public nsIIPCBackgroundChildCreateCallback -{ - nsRefPtr mFactory; - -public: - explicit BackgroundCreateCallback(IDBFactory* aFactory) - : mFactory(aFactory) - { - MOZ_ASSERT(aFactory); - } - - NS_DECL_ISUPPORTS - -private: - ~BackgroundCreateCallback() - { } - - NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK -}; - -struct IDBFactory::PendingRequestInfo -{ - nsRefPtr mRequest; - FactoryRequestParams mParams; - - PendingRequestInfo(IDBOpenDBRequest* aRequest, - const FactoryRequestParams& aParams) - : mRequest(aRequest), mParams(aParams) - { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); - } -}; - IDBFactory::IDBFactory() - : mOwningObject(nullptr) - , mBackgroundActor(nullptr) - , mRootedOwningObject(false) - , mBackgroundActorFailed(false) - , mPrivateBrowsingMode(false) +: mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY), + mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr), + mContentParent(nullptr), mRootedOwningObject(false) { -#ifdef DEBUG - mOwningThread = PR_GetCurrentThread(); -#endif - AssertIsOnOwningThread(); - SetIsDOMBinding(); } IDBFactory::~IDBFactory() { - MOZ_ASSERT_IF(mBackgroundActorFailed, !mBackgroundActor); - + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->Send__delete__(mActorChild); + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); + } if (mRootedOwningObject) { mOwningObject = nullptr; mozilla::DropJSObjects(this); } - - if (mBackgroundActor) { - mBackgroundActor->SendDeleteMeInternal(); - MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); - } } // static nsresult -IDBFactory::CreateForWindow(nsPIDOMWindow* aWindow, - IDBFactory** aFactory) +IDBFactory::Create(nsPIDOMWindow* aWindow, + const nsACString& aGroup, + const nsACString& aASCIIOrigin, + nsIContentParent* aContentParent, + IDBFactory** aFactory) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aWindow); - MOZ_ASSERT(aWindow->IsInnerWindow()); - MOZ_ASSERT(aFactory); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(), + "Non-chrome may not supply their own origin!"); - if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) { - *aFactory = nullptr; - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (aWindow->IsOuterWindow()) { + aWindow = aWindow->GetCurrentInnerWindow(); + IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } - nsCOMPtr sop = do_QueryInterface(aWindow); - if (NS_WARN_IF(!sop)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + // Make sure that the manager is up before we do anything here since lots of + // decisions depend on which process we're running in. + indexedDB::IndexedDatabaseManager* mgr = + indexedDB::IndexedDatabaseManager::GetOrCreate(); + IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsresult rv; + + nsCString group(aGroup); + nsCString origin(aASCIIOrigin); + StoragePrivilege privilege; + PersistenceType defaultPersistenceType; + if (origin.IsEmpty()) { + NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!"); + + rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege, + &defaultPersistenceType); } - - nsCOMPtr principal = sop->GetPrincipal(); - if (NS_WARN_IF(!principal)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + else { + rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege, + &defaultPersistenceType); } - - nsAutoPtr principalInfo(new PrincipalInfo()); - - if (NS_WARN_IF(NS_FAILED(GetPrincipalInfoFromPrincipal(principal, - principalInfo)))) { + if (NS_FAILED(rv)) { // Not allowed. *aFactory = nullptr; return NS_OK; } - IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); - if (NS_WARN_IF(!mgr)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr webNav = do_GetInterface(aWindow); - nsCOMPtr loadContext = do_QueryInterface(webNav); - - bool privateBrowsingMode = loadContext && loadContext->UsePrivateBrowsing(); - nsRefPtr factory = new IDBFactory(); - factory->mPrincipalInfo = Move(principalInfo); + factory->mGroup = group; + factory->mASCIIOrigin = origin; + factory->mPrivilege = privilege; + factory->mDefaultPersistenceType = defaultPersistenceType; factory->mWindow = aWindow; - factory->mTabChild = TabChild::GetFrom(aWindow); - factory->mPrivateBrowsingMode = privateBrowsingMode; + factory->mContentParent = aContentParent; + + if (!IndexedDatabaseManager::IsMainProcess()) { + TabChild* tabChild = TabChild::GetFrom(aWindow); + IDB_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + IndexedDBChild* actor = new IndexedDBChild(tabChild, origin); + + bool allowed; + tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed); + + if (!allowed) { + actor->Send__delete__(actor); + *aFactory = nullptr; + return NS_OK; + } + + actor->SetFactory(factory); + } factory.forget(aFactory); return NS_OK; @@ -196,83 +176,103 @@ IDBFactory::CreateForWindow(nsPIDOMWindow* aWindow, // static nsresult -IDBFactory::CreateForChromeJS(JSContext* aCx, - JS::Handle aOwningObject, - IDBFactory** aFactory) +IDBFactory::Create(JSContext* aCx, + JS::Handle aOwningObject, + nsIContentParent* aContentParent, + IDBFactory** aFactory) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aCx, "Null context!"); + NS_ASSERTION(aOwningObject, "Null object!"); + NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, + "Not a global object!"); + NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); - nsAutoPtr principalInfo( - new PrincipalInfo(SystemPrincipalInfo())); + // Make sure that the manager is up before we do anything here since lots of + // decisions depend on which process we're running in. + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - nsresult rv = - CreateForJSInternal(aCx, aOwningObject, principalInfo, aFactory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + nsCString group; + nsCString origin; + StoragePrivilege privilege; + PersistenceType defaultPersistenceType; + QuotaManager::GetInfoForChrome(&group, &origin, &privilege, + &defaultPersistenceType); + + nsRefPtr factory = new IDBFactory(); + factory->mGroup = group; + factory->mASCIIOrigin = origin; + factory->mPrivilege = privilege; + factory->mDefaultPersistenceType = defaultPersistenceType; + factory->mOwningObject = aOwningObject; + factory->mContentParent = aContentParent; + + mozilla::HoldJSObjects(factory.get()); + factory->mRootedOwningObject = true; + + if (!IndexedDatabaseManager::IsMainProcess()) { + ContentChild* contentChild = ContentChild::GetSingleton(); + IDB_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + IndexedDBChild* actor = new IndexedDBChild(contentChild, origin); + + contentChild->SendPIndexedDBConstructor(actor); + + actor->SetFactory(factory); } - MOZ_ASSERT(!principalInfo); - - return NS_OK; -} - -nsresult -IDBFactory::CreateForDatastore(JSContext* aCx, - JS::Handle aOwningObject, - IDBFactory** aFactory) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // There should be a null principal pushed here, but it's still chrome... - MOZ_ASSERT(!nsContentUtils::IsCallerChrome()); - - nsAutoPtr principalInfo( - new PrincipalInfo(SystemPrincipalInfo())); - - nsresult rv = - CreateForJSInternal(aCx, aOwningObject, principalInfo, aFactory); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!principalInfo); - + factory.forget(aFactory); return NS_OK; } // static nsresult -IDBFactory::CreateForJSInternal(JSContext* aCx, - JS::Handle aOwningObject, - nsAutoPtr& aPrincipalInfo, - IDBFactory** aFactory) +IDBFactory::Create(nsIContentParent* aContentParent, + IDBFactory** aFactory) { - MOZ_ASSERT(aCx); - MOZ_ASSERT(aOwningObject); - MOZ_ASSERT(aPrincipalInfo); - MOZ_ASSERT(aFactory); - MOZ_ASSERT(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, - "Not a global object!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); - if (!NS_IsMainThread()) { - MOZ_CRASH("Not yet supported off the main thread!"); - } + // We need to get this information before we push a null principal to avoid + // IsCallerChrome() assertion in quota manager. + nsCString group; + nsCString origin; + StoragePrivilege privilege; + PersistenceType defaultPersistenceType; + QuotaManager::GetInfoForChrome(&group, &origin, &privilege, + &defaultPersistenceType); - if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) { - *aFactory = nullptr; - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } + nsCOMPtr principal = + do_CreateInstance("@mozilla.org/nullprincipal;1"); + NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE); - IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); - if (NS_WARN_IF(!mgr)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + AutoSafeJSContext cx; + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + NS_ASSERTION(xpc, "This should never be null!"); + + nsCOMPtr globalHolder; + nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder)); + NS_ENSURE_SUCCESS(rv, rv); + + JS::Rooted global(cx, globalHolder->GetJSObject()); + NS_ENSURE_STATE(global); + + // The CreateSandbox call returns a proxy to the actual sandbox object. We + // don't need a proxy here. + global = js::UncheckedUnwrap(global); + + JSAutoCompartment ac(cx, global); nsRefPtr factory = new IDBFactory(); - factory->mPrincipalInfo = aPrincipalInfo.forget(); - factory->mOwningObject = aOwningObject; + factory->mGroup = group; + factory->mASCIIOrigin = origin; + factory->mPrivilege = privilege; + factory->mDefaultPersistenceType = defaultPersistenceType; + factory->mOwningObject = global; + factory->mContentParent = aContentParent; mozilla::HoldJSObjects(factory.get()); factory->mRootedOwningObject = true; @@ -281,423 +281,263 @@ IDBFactory::CreateForJSInternal(JSContext* aCx, return NS_OK; } -#ifdef DEBUG - -void -IDBFactory::AssertIsOnOwningThread() const +// static +already_AddRefed +IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin) { - MOZ_ASSERT(mOwningThread); - MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); + nsCOMPtr uri; + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr fileUrl = do_QueryInterface(uri); + NS_ASSERTION(fileUrl, "This should always succeed!"); + + nsAutoCString type; + PersistenceTypeToText(aPersistenceType, type); + + rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + + NS_LITERAL_CSTRING("&group=") + aGroup + + NS_LITERAL_CSTRING("&origin=") + aOrigin); + NS_ENSURE_SUCCESS(rv, nullptr); + + return fileUrl.forget(); } -#endif // DEBUG - -void -IDBFactory::SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor) +// static +already_AddRefed +IDBFactory::GetConnection(const nsAString& aDatabaseFilePath, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!mBackgroundActor); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")), + "Bad file path!"); - mBackgroundActor = aBackgroundActor; -} - -already_AddRefed -IDBFactory::Open(const nsAString& aName, - uint64_t aVersion, - ErrorResult& aRv) -{ - return OpenInternal(/* aPrincipal */ nullptr, - aName, - Optional(aVersion), - Optional(), - /* aDeleting */ false, - aRv); -} - -already_AddRefed -IDBFactory::Open(const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) -{ - return OpenInternal(/* aPrincipal */ nullptr, - aName, - aOptions.mVersion, - aOptions.mStorage, - /* aDeleting */ false, - aRv); -} - -already_AddRefed -IDBFactory::DeleteDatabase(const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) -{ - return OpenInternal(/* aPrincipal */ nullptr, - aName, - Optional(), - aOptions.mStorage, - /* aDeleting */ true, - aRv); -} - -int16_t -IDBFactory::Cmp(JSContext* aCx, JS::Handle aFirst, - JS::Handle aSecond, ErrorResult& aRv) -{ - Key first, second; - nsresult rv = first.SetFromJSVal(aCx, aFirst); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return 0; - } - - rv = second.SetFromJSVal(aCx, aSecond); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return 0; - } - - if (first.IsUnset() || second.IsUnset()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); - return 0; - } - - return Key::CompareKeys(first, second); -} - -already_AddRefed -IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, - const nsAString& aName, - uint64_t aVersion, - ErrorResult& aRv) -{ - MOZ_ASSERT(aPrincipal); - if (!NS_IsMainThread()) { - MOZ_CRASH("Figure out security checks for workers!"); - } - MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - - return OpenInternal(aPrincipal, - aName, - Optional(aVersion), - Optional(), - /* aDeleting */ false, - aRv); -} - -already_AddRefed -IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, - const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) -{ - MOZ_ASSERT(aPrincipal); - if (!NS_IsMainThread()) { - MOZ_CRASH("Figure out security checks for workers!"); - } - MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - - return OpenInternal(aPrincipal, - aName, - aOptions.mVersion, - aOptions.mStorage, - /* aDeleting */ false, - aRv); -} - -already_AddRefed -IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, - const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) -{ - MOZ_ASSERT(aPrincipal); - if (!NS_IsMainThread()) { - MOZ_CRASH("Figure out security checks for workers!"); - } - MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - - return OpenInternal(aPrincipal, - aName, - Optional(), - aOptions.mStorage, - /* aDeleting */ true, - aRv); -} - -already_AddRefed -IDBFactory::OpenInternal(nsIPrincipal* aPrincipal, - const nsAString& aName, - const Optional& aVersion, - const Optional& aStorageType, - bool aDeleting, - ErrorResult& aRv) -{ - MOZ_ASSERT(mWindow || mOwningObject); - MOZ_ASSERT_IF(!mWindow, !mPrivateBrowsingMode); - - CommonFactoryRequestParams commonParams; - commonParams.privateBrowsingMode() = mPrivateBrowsingMode; - - PrincipalInfo& principalInfo = commonParams.principalInfo(); - - if (aPrincipal) { - if (!NS_IsMainThread()) { - MOZ_CRASH("Figure out security checks for workers!"); - } - MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - - if (NS_WARN_IF(NS_FAILED(GetPrincipalInfoFromPrincipal(aPrincipal, - &principalInfo)))) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - } else { - principalInfo = *mPrincipalInfo; - } - - uint64_t version = 0; - if (!aDeleting && aVersion.WasPassed()) { - if (aVersion.Value() < 1) { - aRv.ThrowTypeError(MSG_INVALID_VERSION); - return nullptr; - } - version = aVersion.Value(); - } - - // Nothing can be done here if we have previously failed to create a - // background actor. - if (mBackgroundActorFailed) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - // XXX We need a bug to switch to temporary storage by default. - - PersistenceType persistenceType; - bool persistenceTypeIsExplicit; - - if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { - // Chrome privilege always gets persistent storage. - persistenceType = PERSISTENCE_TYPE_PERSISTENT; - persistenceTypeIsExplicit = false; - } else { - persistenceType = - PersistenceTypeFromStorage(aStorageType, PERSISTENCE_TYPE_PERSISTENT); - persistenceTypeIsExplicit = aStorageType.WasPassed(); - } - - DatabaseMetadata& metadata = commonParams.metadata(); - metadata.name() = aName; - metadata.persistenceType() = persistenceType; - metadata.persistenceTypeIsExplicit() = persistenceTypeIsExplicit; - - FactoryRequestParams params; - if (aDeleting) { - metadata.version() = 0; - params = DeleteDatabaseRequestParams(commonParams); - } else { - metadata.version() = version; - params = OpenDatabaseRequestParams(commonParams); - } - - if (!mBackgroundActor) { - // If another consumer has already created a background actor for this - // thread then we can start this request immediately. - if (PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread()) { - nsresult rv = BackgroundActorCreated(bgActor); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - MOZ_ASSERT(mBackgroundActor); - } else if (mPendingRequests.IsEmpty()) { - // We need to start the sequence to create a background actor for this - // thread. - nsRefPtr cb = - new BackgroundCreateCallback(this); - if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(cb))) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - } - } - - AutoJSAPI autoJS; - nsRefPtr request; - - if (mWindow) { - if (NS_WARN_IF(!autoJS.Init(mWindow))) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - JS::Rooted scriptOwner(autoJS.cx(), - static_cast(mWindow.get())->FastGetGlobalJSObject()); - MOZ_ASSERT(scriptOwner); - - request = IDBOpenDBRequest::CreateForWindow(this, mWindow, scriptOwner); - } else { - autoJS.Init(); - JS::Rooted scriptOwner(autoJS.cx(), mOwningObject); - - request = IDBOpenDBRequest::CreateForJS(this, scriptOwner); - } - - MOZ_ASSERT(request); - - // If we already have a background actor then we can start this request now. - if (mBackgroundActor) { - nsresult rv = InitiateRequest(request, params); - if (NS_WARN_IF(NS_FAILED(rv))) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - } else { - mPendingRequests.AppendElement(new PendingRequestInfo(request, params)); - } - -#ifdef IDB_PROFILER_USE_MARKS - { - NS_ConvertUTF16toUTF8 profilerName(aName); - if (aDeleting) { - IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")", - "MT IDBFactory.deleteDatabase()", - request->GetSerialNumber(), profilerName.get()); - } else { - IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)", - "MT IDBFactory.open()", - request->GetSerialNumber(), profilerName.get(), - aVersion); - } - } -#endif - - return request.forget(); + nsCOMPtr dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); + NS_ENSURE_TRUE(dbFile, nullptr); + + nsresult rv = dbFile->InitWithPath(aDatabaseFilePath); + NS_ENSURE_SUCCESS(rv, nullptr); + + bool exists; + rv = dbFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, nullptr); + NS_ENSURE_TRUE(exists, nullptr); + + nsCOMPtr dbFileUrl = + GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin); + NS_ENSURE_TRUE(dbFileUrl, nullptr); + + nsCOMPtr ss = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(ss, nullptr); + + nsCOMPtr connection; + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = SetDefaultPragmas(connection); + NS_ENSURE_SUCCESS(rv, nullptr); + + return connection.forget(); } +// static nsresult -IDBFactory::BackgroundActorCreated(PBackgroundChild* aBackgroundActor) +IDBFactory::SetDefaultPragmas(mozIStorageConnection* aConnection) { - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!mBackgroundActor); - MOZ_ASSERT(!mBackgroundActorFailed); + NS_ASSERTION(aConnection, "Null connection!"); - { - BackgroundFactoryChild* actor = new BackgroundFactoryChild(this); + static const char query[] = +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) + // Switch the journaling mode to TRUNCATE to avoid changing the directory + // structure at the conclusion of every transaction for devices with slower + // file systems. + "PRAGMA journal_mode = TRUNCATE; " +#endif + // We use foreign keys in lots of places. + "PRAGMA foreign_keys = ON; " + // The "INSERT OR REPLACE" statement doesn't fire the update trigger, + // instead it fires only the insert trigger. This confuses the update + // refcount function. This behavior changes with enabled recursive triggers, + // so the statement fires the delete trigger first and then the insert + // trigger. + "PRAGMA recursive_triggers = ON;"; - MOZ_ASSERT(NS_IsMainThread(), "Fix this windowId stuff for workers!"); + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - OptionalWindowId windowId; - if (mWindow && IndexedDatabaseManager::IsMainProcess()) { - MOZ_ASSERT(mWindow->IsInnerWindow()); - windowId = mWindow->WindowID(); - } else { - windowId = void_t(); + return NS_OK; +} + +inline +bool +IgnoreWhitespace(char16_t c) +{ + return false; +} + +// static +nsresult +IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, + const nsACString& aDatabaseId, + uint64_t* aVersion, + ObjectStoreInfoArray& aObjectStores) +{ + AssertIsOnIOThread(); + NS_ASSERTION(aConnection, "Null pointer!"); + + aObjectStores.Clear(); + + // Load object store names and ids. + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, id, key_path, auto_increment " + "FROM object_store" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoTArray infoMap; + + bool hasResult; + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + nsRefPtr* element = + aObjectStores.AppendElement(new ObjectStoreInfo()); + + ObjectStoreInfo* info = element->get(); + + rv = stmt->GetString(0, info->name); + NS_ENSURE_SUCCESS(rv, rv); + + info->id = stmt->AsInt64(1); + + int32_t columnType; + nsresult rv = stmt->GetTypeOfIndex(2, &columnType); + NS_ENSURE_SUCCESS(rv, rv); + + // NB: We don't have to handle the NULL case, since that is the default + // for a new KeyPath. + if (columnType != mozIStorageStatement::VALUE_TYPE_NULL) { + NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT, + "Should be a string"); + nsString keyPathSerialization; + rv = stmt->GetString(2, keyPathSerialization); + NS_ENSURE_SUCCESS(rv, rv); + + info->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); } - mBackgroundActor = - static_cast( - aBackgroundActor->SendPBackgroundIDBFactoryConstructor(actor, - windowId)); + info->nextAutoIncrementId = stmt->AsInt64(3); + info->comittedAutoIncrementId = info->nextAutoIncrementId; + + info->autoIncrement = !!info->nextAutoIncrementId; + + ObjectStoreInfoMap* mapEntry = infoMap.AppendElement(); + NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY); + + mapEntry->id = info->id; + mapEntry->info = info; } + NS_ENSURE_SUCCESS(rv, rv); - if (NS_WARN_IF(!mBackgroundActor)) { - BackgroundActorFailed(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + // Load index information + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT object_store_id, id, name, key_path, unique_index, multientry " + "FROM object_store_index" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); - nsresult rv = NS_OK; + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + int64_t objectStoreId = stmt->AsInt64(0); - for (uint32_t index = 0, count = mPendingRequests.Length(); - index < count; - index++) { - nsAutoPtr info(mPendingRequests[index].forget()); - - nsresult rv2 = InitiateRequest(info->mRequest, info->mParams); - - // Warn for every failure, but just return the first failure if there are - // multiple failures. - if (NS_WARN_IF(NS_FAILED(rv2)) && NS_SUCCEEDED(rv)) { - rv = rv2; + ObjectStoreInfo* objectStoreInfo = nullptr; + for (uint32_t index = 0; index < infoMap.Length(); index++) { + if (infoMap[index].id == objectStoreId) { + objectStoreInfo = infoMap[index].info; + break; + } } + + if (!objectStoreInfo) { + NS_ERROR("Index for nonexistant object store!"); + return NS_ERROR_UNEXPECTED; + } + + IndexInfo* indexInfo = objectStoreInfo->indexes.AppendElement(); + NS_ENSURE_TRUE(indexInfo, NS_ERROR_OUT_OF_MEMORY); + + indexInfo->id = stmt->AsInt64(1); + + rv = stmt->GetString(2, indexInfo->name); + NS_ENSURE_SUCCESS(rv, rv); + + nsString keyPathSerialization; + rv = stmt->GetString(3, keyPathSerialization); + NS_ENSURE_SUCCESS(rv, rv); + + // XXX bent wants to assert here + indexInfo->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); + indexInfo->unique = !!stmt->AsInt32(4); + indexInfo->multiEntry = !!stmt->AsInt32(5); + } + NS_ENSURE_SUCCESS(rv, rv); + + // Load version information. + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT version " + "FROM database" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, rv); + + if (!hasResult) { + NS_ERROR("Database has no version!"); + return NS_ERROR_UNEXPECTED; } - mPendingRequests.Clear(); + int64_t version = 0; + rv = stmt->GetInt64(0, &version); + + *aVersion = std::max(version, 0); return rv; } -void -IDBFactory::BackgroundActorFailed() -{ - MOZ_ASSERT(!mPendingRequests.IsEmpty()); - MOZ_ASSERT(!mBackgroundActor); - MOZ_ASSERT(!mBackgroundActorFailed); - - mBackgroundActorFailed = true; - - for (uint32_t index = 0, count = mPendingRequests.Length(); - index < count; - index++) { - nsAutoPtr info(mPendingRequests[index].forget()); - info->mRequest-> - DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - mPendingRequests.Clear(); -} - +// static nsresult -IDBFactory::InitiateRequest(IDBOpenDBRequest* aRequest, - const FactoryRequestParams& aParams) +IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, + uint64_t aVersion, + ObjectStoreInfoArray& aObjectStores) { - MOZ_ASSERT(aRequest); - MOZ_ASSERT(mBackgroundActor); - MOZ_ASSERT(!mBackgroundActorFailed); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aDatabaseInfo, "Null pointer!"); - bool deleting; - uint64_t requestedVersion; - PersistenceType persistenceType; + ObjectStoreInfoArray objectStores; + objectStores.SwapElements(aObjectStores); - switch (aParams.type()) { - case FactoryRequestParams::TDeleteDatabaseRequestParams: { - const DatabaseMetadata& metadata = - aParams.get_DeleteDatabaseRequestParams().commonParams().metadata(); - deleting = true; - requestedVersion = metadata.version(); - persistenceType = metadata.persistenceType(); - break; - } - - case FactoryRequestParams::TOpenDatabaseRequestParams: { - const DatabaseMetadata& metadata = - aParams.get_OpenDatabaseRequestParams().commonParams().metadata(); - deleting = false; - requestedVersion = metadata.version(); - persistenceType = metadata.persistenceType(); - break; - } - - default: - MOZ_CRASH("Should never get here!"); +#ifdef DEBUG + { + nsTArray existingNames; + aDatabaseInfo->GetObjectStoreNames(existingNames); + NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo"); } +#endif - auto actor = - new BackgroundFactoryRequestChild(this, aRequest, deleting, - requestedVersion, persistenceType); + aDatabaseInfo->version = aVersion; - if (!mBackgroundActor->SendPBackgroundIDBFactoryRequestConstructor(actor, - aParams)) { - aRequest->DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + for (uint32_t index = 0; index < objectStores.Length(); index++) { + nsRefPtr& info = objectStores[index]; + + if (!aDatabaseInfo->PutObjectStore(info)) { + NS_WARNING("Out of memory!"); + return NS_ERROR_OUT_OF_MEMORY; + } } return NS_OK; @@ -735,38 +575,257 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject) NS_IMPL_CYCLE_COLLECTION_TRACE_END +nsresult +IDBFactory::OpenInternal(const nsAString& aName, + int64_t aVersion, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aASCIIOrigin, + StoragePrivilege aPrivilege, + bool aDeleting, + IDBOpenDBRequest** _retval) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!"); + + AutoJSContext cx; + nsCOMPtr window; + JS::Rooted scriptOwner(cx); + + if (mWindow) { + window = mWindow; + scriptOwner = + static_cast(window.get())->FastGetGlobalJSObject(); + } + else { + scriptOwner = mOwningObject; + } + + if (aPrivilege == Chrome) { + // Chrome privilege, ignore the persistence type parameter. + aPersistenceType = PERSISTENCE_TYPE_PERSISTENT; + } + + nsRefPtr request = + IDBOpenDBRequest::Create(this, window, scriptOwner); + IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsresult rv; + + if (IndexedDatabaseManager::IsMainProcess()) { + nsRefPtr openHelper = + new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion, + aPersistenceType, aDeleting, mContentParent, + aPrivilege); + + rv = openHelper->Init(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) { + openHelper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + rv = openHelper->WaitForOpenAllowed(); + } + else { + if (mPrivilege != Chrome && + aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { + nsRefPtr permissionHelper = + new CheckPermissionsHelper(openHelper, window); + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + rv = quotaManager-> + WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin), + Nullable(aPersistenceType), + openHelper->Id(), permissionHelper); + } + else { + // Chrome and temporary storage doesn't need to check the permission. + rv = openHelper->WaitForOpenAllowed(); + } + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else if (aDeleting) { + nsCString databaseId; + QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, Client::IDB, + aName, databaseId); + MOZ_ASSERT(!databaseId.IsEmpty()); + + IndexedDBDeleteDatabaseRequestChild* actor = + new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId); + + mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor( + actor, + nsString(aName), + aPersistenceType); + } + else { + IndexedDBDatabaseChild* dbActor = + static_cast( + mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName), + aVersion, + aPersistenceType)); + + dbActor->SetRequest(request); + } + +#ifdef IDB_PROFILER_USE_MARKS + { + NS_ConvertUTF16toUTF8 profilerName(aName); + if (aDeleting) { + IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")", + "MT IDBFactory.deleteDatabase()", + request->GetSerialNumber(), profilerName.get()); + } + else { + IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)", + "MT IDBFactory.open()", + request->GetSerialNumber(), profilerName.get(), + aVersion); + } + } +#endif + + request.forget(_retval); + return NS_OK; +} + JSObject* IDBFactory::WrapObject(JSContext* aCx) { return IDBFactoryBinding::Wrap(aCx, this); } -NS_IMPL_ISUPPORTS(IDBFactory::BackgroundCreateCallback, - nsIIPCBackgroundChildCreateCallback) - -void -IDBFactory::BackgroundCreateCallback::ActorCreated(PBackgroundChild* aActor) +already_AddRefed +IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) { - MOZ_ASSERT(aActor); - MOZ_ASSERT(mFactory); - - nsRefPtr factory; - mFactory.swap(factory); - - factory->BackgroundActorCreated(aActor); + return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv); } -void -IDBFactory::BackgroundCreateCallback::ActorFailed() +already_AddRefed +IDBFactory::DeleteDatabase(const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) { - MOZ_ASSERT(mFactory); - - nsRefPtr factory; - mFactory.swap(factory); - - factory->BackgroundActorFailed(); + return Open(nullptr, aName, Optional(), aOptions.mStorage, true, + aRv); } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +int16_t +IDBFactory::Cmp(JSContext* aCx, JS::Handle aFirst, + JS::Handle aSecond, ErrorResult& aRv) +{ + Key first, second; + nsresult rv = first.SetFromJSVal(aCx, aFirst); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return 0; + } + + rv = second.SetFromJSVal(aCx, aSecond); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return 0; + } + + if (first.IsUnset() || second.IsUnset()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); + return 0; + } + + return Key::CompareKeys(first, second); +} + +already_AddRefed +IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, + uint64_t aVersion, ErrorResult& aRv) +{ + // Just to be on the extra-safe side + if (!nsContentUtils::IsCallerChrome()) { + MOZ_CRASH(); + } + + return Open(aPrincipal, aName, Optional(aVersion), + Optional(), false, aRv); +} + +already_AddRefed +IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, + const IDBOpenDBOptions& aOptions, ErrorResult& aRv) +{ + // Just to be on the extra-safe side + if (!nsContentUtils::IsCallerChrome()) { + MOZ_CRASH(); + } + + return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false, + aRv); +} + +already_AddRefed +IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) +{ + // Just to be on the extra-safe side + if (!nsContentUtils::IsCallerChrome()) { + MOZ_CRASH(); + } + + return Open(aPrincipal, aName, Optional(), aOptions.mStorage, true, + aRv); +} + +already_AddRefed +IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName, + const Optional& aVersion, + const Optional& aStorageType, + bool aDelete, ErrorResult& aRv) +{ + nsresult rv; + + nsCString group; + nsCString origin; + StoragePrivilege privilege; + PersistenceType defaultPersistenceType; + if (aPrincipal) { + rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin, + &privilege, + &defaultPersistenceType); + if (NS_FAILED(rv)) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + } + else { + group = mGroup; + origin = mASCIIOrigin; + privilege = mPrivilege; + defaultPersistenceType = mDefaultPersistenceType; + } + + uint64_t version = 0; + if (!aDelete && aVersion.WasPassed()) { + if (aVersion.Value() < 1) { + aRv.ThrowTypeError(MSG_INVALID_VERSION); + return nullptr; + } + version = aVersion.Value(); + } + + PersistenceType persistenceType = + PersistenceTypeFromStorage(aStorageType, defaultPersistenceType); + + nsRefPtr request; + rv = OpenInternal(aName, version, persistenceType, group, origin, privilege, + aDelete, getter_AddRefs(request)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + return request.forget(); +} diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index df3292dd78ec..284df8113268 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -7,203 +7,219 @@ #ifndef mozilla_dom_indexeddb_idbfactory_h__ #define mozilla_dom_indexeddb_idbfactory_h__ -#include "mozilla/Attributes.h" +#include "mozilla/dom/BindingDeclarations.h" // for Optional #include "mozilla/dom/StorageTypeBinding.h" -#include "nsAutoPtr.h" +#include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/dom/quota/StoragePrivilege.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" -#include "nsISupports.h" -#include "nsString.h" -#include "nsTArray.h" #include "nsWrapperCache.h" +class mozIStorageConnection; +class nsIFile; +class nsIFileURL; class nsIPrincipal; class nsPIDOMWindow; -struct PRThread; +template class nsRefPtr; namespace mozilla { - class ErrorResult; -namespace ipc { - -class PBackgroundChild; -class PrincipalInfo; - -} // namespace ipc - namespace dom { - +class nsIContentParent; struct IDBOpenDBOptions; -class TabChild; namespace indexedDB { -class BackgroundFactoryChild; -class FactoryRequestParams; +struct DatabaseInfo; +class IDBDatabase; class IDBOpenDBRequest; +class IndexedDBChild; +class IndexedDBParent; -class IDBFactory MOZ_FINAL - : public nsISupports - , public nsWrapperCache +struct ObjectStoreInfo; + +class IDBFactory MOZ_FINAL : public nsISupports, + public nsWrapperCache { - typedef mozilla::dom::StorageType StorageType; - typedef mozilla::ipc::PBackgroundChild PBackgroundChild; - typedef mozilla::ipc::PrincipalInfo PrincipalInfo; - - class BackgroundCreateCallback; - struct PendingRequestInfo; - - nsAutoPtr mPrincipalInfo; - - // If this factory lives on a window then mWindow must be non-null. Otherwise - // mOwningObject must be non-null. - nsCOMPtr mWindow; - JS::Heap mOwningObject; - - // This will only be set if the factory belongs to a window in a child - // process. - nsRefPtr mTabChild; - - nsTArray> mPendingRequests; - - BackgroundFactoryChild* mBackgroundActor; - -#ifdef DEBUG - PRThread* mOwningThread; -#endif - - bool mRootedOwningObject; - bool mBackgroundActorFailed; - bool mPrivateBrowsingMode; + typedef mozilla::dom::nsIContentParent nsIContentParent; + typedef mozilla::dom::quota::PersistenceType PersistenceType; + typedef nsTArray > ObjectStoreInfoArray; + typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege; public: - static nsresult - CreateForWindow(nsPIDOMWindow* aWindow, - IDBFactory** aFactory); + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory) - static nsresult - CreateForChromeJS(JSContext* aCx, - JS::Handle aOwningObject, - IDBFactory** aFactory); + // Called when using IndexedDB from a window in a different process. + static nsresult Create(nsPIDOMWindow* aWindow, + const nsACString& aGroup, + const nsACString& aASCIIOrigin, + nsIContentParent* aContentParent, + IDBFactory** aFactory); - static nsresult - CreateForDatastore(JSContext* aCx, - JS::Handle aOwningObject, - IDBFactory** aFactory); - - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - void - SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor); - - void - ClearBackgroundActor() + // Called when using IndexedDB from a window in the current process. + static nsresult Create(nsPIDOMWindow* aWindow, + nsIContentParent* aContentParent, + IDBFactory** aFactory) { - AssertIsOnOwningThread(); - - mBackgroundActor = nullptr; + return Create(aWindow, EmptyCString(), EmptyCString(), aContentParent, + aFactory); } + // Called when using IndexedDB from a JS component or a JSM in the current + // process. + static nsresult Create(JSContext* aCx, + JS::Handle aOwningObject, + nsIContentParent* aContentParent, + IDBFactory** aFactory); + + // Called when using IndexedDB from a JS component or a JSM in a different + // process or from a C++ component. + static nsresult Create(nsIContentParent* aContentParent, + IDBFactory** aFactory); + + static already_AddRefed + GetDatabaseFileURL(nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin); + + static already_AddRefed + GetConnection(const nsAString& aDatabaseFilePath, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin); + + static nsresult + SetDefaultPragmas(mozIStorageConnection* aConnection); + + static nsresult + LoadDatabaseInformation(mozIStorageConnection* aConnection, + const nsACString& aDatabaseId, + uint64_t* aVersion, + ObjectStoreInfoArray& aObjectStores); + + static nsresult + SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, + uint64_t aVersion, + ObjectStoreInfoArray& aObjectStores); + + nsresult + OpenInternal(const nsAString& aName, + int64_t aVersion, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aASCIIOrigin, + StoragePrivilege aStoragePrivilege, + bool aDeleting, + IDBOpenDBRequest** _retval); + + nsresult + OpenInternal(const nsAString& aName, + int64_t aVersion, + PersistenceType aPersistenceType, + bool aDeleting, + IDBOpenDBRequest** _retval) + { + return OpenInternal(aName, aVersion, aPersistenceType, mGroup, mASCIIOrigin, + mPrivilege, aDeleting, _retval); + } + + void + SetActor(IndexedDBChild* aActorChild) + { + NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); + mActorChild = aActorChild; + } + + void + SetActor(IndexedDBParent* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, "Shouldn't have more than one!"); + mActorParent = aActorParent; + } + + const nsCString& + GetASCIIOrigin() const + { + return mASCIIOrigin; + } + + bool + FromIPC() + { + return !!mContentParent; + } + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // WebIDL nsPIDOMWindow* GetParentObject() const { return mWindow; } - TabChild* - GetTabChild() const + already_AddRefed + Open(const nsAString& aName, uint64_t aVersion, ErrorResult& aRv) { - return mTabChild; - } - - PrincipalInfo* - GetPrincipalInfo() const - { - AssertIsOnOwningThread(); - - return mPrincipalInfo; + return Open(nullptr, aName, Optional(aVersion), + Optional(), false, aRv); } already_AddRefed - Open(const nsAString& aName, - uint64_t aVersion, + Open(const nsAString& aName, const IDBOpenDBOptions& aOptions, ErrorResult& aRv); already_AddRefed - Open(const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv); - - already_AddRefed - DeleteDatabase(const nsAString& aName, - const IDBOpenDBOptions& aOptions, + DeleteDatabase(const nsAString& aName, const IDBOpenDBOptions& aOptions, ErrorResult& aRv); int16_t - Cmp(JSContext* aCx, - JS::Handle aFirst, - JS::Handle aSecond, - ErrorResult& aRv); + Cmp(JSContext* aCx, JS::Handle aFirst, + JS::Handle aSecond, ErrorResult& aRv); already_AddRefed - OpenForPrincipal(nsIPrincipal* aPrincipal, - const nsAString& aName, - uint64_t aVersion, - ErrorResult& aRv); + OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, + uint64_t aVersion, ErrorResult& aRv); already_AddRefed - OpenForPrincipal(nsIPrincipal* aPrincipal, - const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv); + OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, + const IDBOpenDBOptions& aOptions, ErrorResult& aRv); already_AddRefed - DeleteForPrincipal(nsIPrincipal* aPrincipal, - const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory) - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; + DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, + const IDBOpenDBOptions& aOptions, ErrorResult& aRv); private: IDBFactory(); ~IDBFactory(); - static nsresult - CreateForJSInternal(JSContext* aCx, - JS::Handle aOwningObject, - nsAutoPtr& aPrincipalInfo, - IDBFactory** aFactory); - already_AddRefed - OpenInternal(nsIPrincipal* aPrincipal, - const nsAString& aName, - const Optional& aVersion, - const Optional& aStorageType, - bool aDeleting, - ErrorResult& aRv); + Open(nsIPrincipal* aPrincipal, const nsAString& aName, + const Optional& aVersion, + const Optional& aStorageType, bool aDelete, + ErrorResult& aRv); - nsresult - BackgroundActorCreated(PBackgroundChild* aBackgroundActor); + nsCString mGroup; + nsCString mASCIIOrigin; + StoragePrivilege mPrivilege; + PersistenceType mDefaultPersistenceType; - void - BackgroundActorFailed(); + // If this factory lives on a window then mWindow must be non-null. Otherwise + // mOwningObject must be non-null. + nsCOMPtr mWindow; + JS::Heap mOwningObject; - nsresult - InitiateRequest(IDBOpenDBRequest* aRequest, - const FactoryRequestParams& aParams); + IndexedDBChild* mActorChild; + IndexedDBParent* mActorParent; + + mozilla::dom::nsIContentParent* mContentParent; + + bool mRootedOwningObject; }; } // namespace indexedDB diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index eb043e829a1a..336aa2f007bd 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -16,16 +16,16 @@ #include "nsServiceManagerUtils.h" #include "nsWidgetsCID.h" -namespace mozilla { -namespace dom { -namespace indexedDB { - namespace { NS_DEFINE_CID(kAppShellCID2, NS_APPSHELL_CID); } // anonymous namespace +namespace mozilla { +namespace dom { +namespace indexedDB { + IDBFileHandle::IDBFileHandle(FileMode aMode, RequestMode aRequestMode, IDBMutableFile* aMutableFile) @@ -158,10 +158,10 @@ IDBFileHandle::OnCompleteOrAbort(bool aAborted) { nsCOMPtr event; if (aAborted) { - event = CreateGenericEvent(this, nsDependentString(kAbortEventType), + event = CreateGenericEvent(this, NS_LITERAL_STRING(ABORT_EVT_STR), eDoesBubble, eNotCancelable); } else { - event = CreateGenericEvent(this, nsDependentString(kCompleteEventType), + event = CreateGenericEvent(this, NS_LITERAL_STRING(COMPLETE_EVT_STR), eDoesNotBubble, eNotCancelable); } if (NS_WARN_IF(!event)) { diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index 1d0cda770337..a2a3e154a59d 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -4,226 +4,432 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "base/basictypes.h" + #include "IDBIndex.h" -#include "FileInfo.h" +#include +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ipc/Blob.h" +#include "mozilla/storage.h" +#include "nsThreadUtils.h" +#include "xpcpublic.h" + +#include "AsyncConnectionHelper.h" +#include "DatabaseInfo.h" #include "IDBCursor.h" #include "IDBEvents.h" #include "IDBKeyRange.h" #include "IDBObjectStore.h" -#include "IDBRequest.h" #include "IDBTransaction.h" -#include "IndexedDatabase.h" -#include "IndexedDatabaseInlines.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -// Include this last to avoid path problems on Windows. -#include "ActorsChild.h" +#include "ipc/IndexedDBChild.h" +#include "ipc/IndexedDBParent.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "IndexedDatabaseInlines.h" + +USING_INDEXEDDB_NAMESPACE +using namespace mozilla::dom; +using namespace mozilla::dom::indexedDB::ipc; +using mozilla::ErrorResult; +using mozilla::Move; namespace { +class IndexHelper : public AsyncConnectionHelper +{ +public: + IndexHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex) + : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex), + mActor(nullptr) + { + NS_ASSERTION(aTransaction, "Null transaction!"); + NS_ASSERTION(aRequest, "Null request!"); + NS_ASSERTION(aIndex, "Null index!"); + } + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) = 0; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; + +protected: + nsRefPtr mIndex; + +private: + IndexedDBIndexRequestChild* mActor; +}; + +class GetKeyHelper : public IndexHelper +{ +public: + GetKeyHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange) + : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + // In-params. + nsRefPtr mKeyRange; + + // Out-params. + Key mKey; +}; + +class GetHelper : public GetKeyHelper +{ +public: + GetHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange) + : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange) + { } + + ~GetHelper() + { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + StructuredCloneReadInfo mCloneReadInfo; +}; + +class GetAllKeysHelper : public GetKeyHelper +{ +public: + GetAllKeysHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange, + const uint32_t aLimit) + : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + const uint32_t mLimit; + nsTArray mKeys; +}; + +class GetAllHelper : public GetKeyHelper +{ +public: + GetAllHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange, + const uint32_t aLimit) + : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit) + { } + + ~GetAllHelper() + { + for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); + } + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + const uint32_t mLimit; + nsTArray mCloneReadInfos; +}; + +class OpenKeyCursorHelper : public IndexHelper +{ +public: + OpenKeyCursorHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange, + IDBCursor::Direction aDirection) + : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange), + mDirection(aDirection) + { } + + ~OpenKeyCursorHelper() + { + NS_ASSERTION(true, "bas"); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + virtual nsresult EnsureCursor(); + + // In-params. + nsRefPtr mKeyRange; + const IDBCursor::Direction mDirection; + + // Out-params. + Key mKey; + Key mObjectKey; + nsCString mContinueQuery; + nsCString mContinueToQuery; + Key mRangeKey; + + // Only used in the parent process. + nsRefPtr mCursor; +}; + +class OpenCursorHelper : public OpenKeyCursorHelper +{ +public: + OpenCursorHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange, + IDBCursor::Direction aDirection) + : OpenKeyCursorHelper(aTransaction, aRequest, aIndex, aKeyRange, aDirection) + { } + + ~OpenCursorHelper() + { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + +private: + virtual nsresult EnsureCursor(); + + StructuredCloneReadInfo mCloneReadInfo; + + // Only used in the parent process. + SerializedStructuredCloneReadInfo mSerializedCloneReadInfo; +}; + +class CountHelper : public IndexHelper +{ +public: + CountHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBIndex* aIndex, + IDBKeyRange* aKeyRange) + : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange), mCount(0) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +private: + nsRefPtr mKeyRange; + uint64_t mCount; +}; + +inline already_AddRefed GenerateRequest(IDBIndex* aIndex) { - MOZ_ASSERT(aIndex); - aIndex->AssertIsOnOwningThread(); - + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = aIndex->ObjectStore()->Transaction(); - - nsRefPtr request = - IDBRequest::Create(aIndex, transaction->Database(), transaction); - MOZ_ASSERT(request); - - return request.forget(); + IDBDatabase* database = transaction->Database(); + return IDBRequest::Create(aIndex, database, transaction); } } // anonymous namespace -IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata) - : mObjectStore(aObjectStore) - , mCachedKeyPath(JSVAL_VOID) - , mMetadata(aMetadata) - , mId(aMetadata->id()) - , mRooted(false) +// static +already_AddRefed +IDBIndex::Create(IDBObjectStore* aObjectStore, + const IndexInfo* aIndexInfo, + bool aCreating) { - MOZ_ASSERT(aObjectStore); - aObjectStore->AssertIsOnOwningThread(); - MOZ_ASSERT(aMetadata); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aObjectStore, "Null pointer!"); + NS_ASSERTION(aIndexInfo, "Null pointer!"); + + nsRefPtr index = new IDBIndex(); + + index->mObjectStore = aObjectStore; + index->mId = aIndexInfo->id; + index->mName = aIndexInfo->name; + index->mKeyPath = aIndexInfo->keyPath; + index->mUnique = aIndexInfo->unique; + index->mMultiEntry = aIndexInfo->multiEntry; + + if (!IndexedDatabaseManager::IsMainProcess()) { + IndexedDBObjectStoreChild* objectStoreActor = aObjectStore->GetActorChild(); + NS_ASSERTION(objectStoreActor, "Must have an actor here!"); + + nsAutoPtr actor(new IndexedDBIndexChild(index)); + + IndexConstructorParams params; + + if (aCreating) { + CreateIndexParams createParams; + createParams.info() = *aIndexInfo; + params = createParams; + } + else { + GetIndexParams getParams; + getParams.name() = aIndexInfo->name; + params = getParams; + } + + objectStoreActor->SendPIndexedDBIndexConstructor(actor.forget(), params); + } + + return index.forget(); +} + +IDBIndex::IDBIndex() +: mId(INT64_MIN), + mKeyPath(0), + mCachedKeyPath(JSVAL_VOID), + mActorChild(nullptr), + mActorParent(nullptr), + mUnique(false), + mMultiEntry(false), + mRooted(false) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); SetIsDOMBinding(); } IDBIndex::~IDBIndex() { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); if (mRooted) { mCachedKeyPath = JSVAL_VOID; mozilla::DropJSObjects(this); } -} -already_AddRefed -IDBIndex::Create(IDBObjectStore* aObjectStore, - const IndexMetadata& aMetadata) -{ - MOZ_ASSERT(aObjectStore); - aObjectStore->AssertIsOnOwningThread(); - - nsRefPtr index = new IDBIndex(aObjectStore, &aMetadata); - - return index.forget(); -} - -#ifdef DEBUG - -void -IDBIndex::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mObjectStore); - mObjectStore->AssertIsOnOwningThread(); -} - -#endif // DEBUG - -void -IDBIndex::RefreshMetadata(bool aMayDelete) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(mDeletedMetadata, mMetadata == mDeletedMetadata); - - const nsTArray& indexes = mObjectStore->Spec().indexes(); - - bool found = false; - - for (uint32_t count = indexes.Length(), index = 0; - index < count; - index++) { - const IndexMetadata& metadata = indexes[index]; - - if (metadata.id() == Id()) { - mMetadata = &metadata; - - found = true; - break; - } + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->Send__delete__(mActorChild); + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); } - - MOZ_ASSERT_IF(!aMayDelete && !mDeletedMetadata, found); - - if (found) { - MOZ_ASSERT(mMetadata != mDeletedMetadata); - mDeletedMetadata = nullptr; - } else { - NoteDeletion(); - } -} - -void -IDBIndex::NoteDeletion() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mMetadata); - MOZ_ASSERT(Id() == mMetadata->id()); - - if (mDeletedMetadata) { - MOZ_ASSERT(mMetadata == mDeletedMetadata); - return; - } - - mDeletedMetadata = new IndexMetadata(*mMetadata); - - mMetadata = mDeletedMetadata; -} - -const nsString& -IDBIndex::Name() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mMetadata); - - return mMetadata->name(); -} - -bool -IDBIndex::Unique() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mMetadata); - - return mMetadata->unique(); -} - -bool -IDBIndex::MultiEntry() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mMetadata); - - return mMetadata->multiEntry(); -} - -const KeyPath& -IDBIndex::GetKeyPath() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mMetadata); - - return mMetadata->keyPath(); -} - -nsPIDOMWindow* -IDBIndex::GetParentObject() const -{ - AssertIsOnOwningThread(); - - return mObjectStore->GetParentObject(); -} - -void -IDBIndex::GetKeyPath(JSContext* aCx, - JS::MutableHandle aResult, - ErrorResult& aRv) -{ - AssertIsOnOwningThread(); - - if (!mCachedKeyPath.isUndefined()) { - MOZ_ASSERT(mRooted); - JS::ExposeValueToActiveJS(mCachedKeyPath); - aResult.set(mCachedKeyPath); - return; - } - - MOZ_ASSERT(!mRooted); - - aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - if (mCachedKeyPath.isGCThing()) { - mozilla::HoldJSObjects(this); - mRooted = true; - } - - JS::ExposeValueToActiveJS(mCachedKeyPath); - aResult.set(mCachedKeyPath); } already_AddRefed -IDBIndex::GetInternal(bool aKeyOnly, - JSContext* aCx, - JS::Handle aKey, - ErrorResult& aRv) +IDBIndex::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -231,79 +437,84 @@ IDBIndex::GetInternal(bool aKeyOnly, return nullptr; } - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - if (!keyRange) { - // Must specify a key or keyRange for get() and getKey(). - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); - return nullptr; - } - - const int64_t objectStoreId = mObjectStore->Id(); - const int64_t indexId = Id(); - - OptionalKeyRange optionalKeyRange; - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - optionalKeyRange = serializedKeyRange; - } else { - optionalKeyRange = void_t(); - } - - RequestParams params; - - if (aKeyOnly) { - params = IndexGetKeyParams(objectStoreId, indexId, optionalKeyRange); - } else { - params = IndexGetParams(objectStoreId, indexId, optionalKeyRange); - } - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - transaction->StartRequest(actor, params); - - if (aKeyOnly) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "getKey(%s)", - "IDBRequest[%llu] MT IDBIndex.getKey()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(transaction->Database()), - IDB_PROFILER_STRING(transaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKey)); - } else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "get(%s)", - "IDBRequest[%llu] MT IDBIndex.get()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(transaction->Database()), - IDB_PROFILER_STRING(transaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKey)); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } + + nsRefPtr helper = + new GetHelper(transaction, request, this, aKeyRange); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "get(%s)", + "IDBRequest[%llu] MT IDBIndex.get()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + return request.forget(); } already_AddRefed -IDBIndex::GetAllInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, +IDBIndex::GetKeyInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + nsRefPtr helper = + new GetKeyHelper(transaction, request, this, aKeyRange); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getKey(%s)", + "IDBRequest[%llu] MT IDBIndex.getKey()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + + return request.forget(); +} + +already_AddRefed +IDBIndex::GetAllInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -311,78 +522,43 @@ IDBIndex::GetAllInternal(bool aKeysOnly, return nullptr; } - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (NS_WARN_IF(aRv.Failed())) { + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - const int64_t objectStoreId = mObjectStore->Id(); - const int64_t indexId = Id(); + nsRefPtr helper = + new GetAllHelper(transaction, request, this, aKeyRange, aLimit); - OptionalKeyRange optionalKeyRange; - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - optionalKeyRange = serializedKeyRange; - } else { - optionalKeyRange = void_t(); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } - const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0; - - RequestParams params; - if (aKeysOnly) { - params = IndexGetAllKeysParams(objectStoreId, indexId, optionalKeyRange, - limit); - } else { - params = IndexGetAllParams(objectStoreId, indexId, optionalKeyRange, limit); - } - - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - transaction->StartRequest(actor, params); - - if (aKeysOnly) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "getAllKeys(%s, %lu)", - "IDBRequest[%llu] MT IDBIndex.getAllKeys()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(transaction->Database()), - IDB_PROFILER_STRING(transaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKeyRange), - aLimit); - } else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "getAll(%s, %lu)", - "IDBRequest[%llu] MT IDBIndex.getAll()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(transaction->Database()), - IDB_PROFILER_STRING(transaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKeyRange), - aLimit); - } + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getAll(%s, %lu)", + "IDBRequest[%llu] MT IDBIndex.getAll()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKeyRange), aLimit); return request.forget(); } already_AddRefed -IDBIndex::OpenCursorInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, +IDBIndex::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -390,64 +566,42 @@ IDBIndex::OpenCursorInternal(bool aKeysOnly, return nullptr; } - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); - if (NS_WARN_IF(aRv.Failed())) { + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - int64_t objectStoreId = mObjectStore->Id(); - int64_t indexId = Id(); + nsRefPtr helper = + new GetAllKeysHelper(transaction, request, this, aKeyRange, aLimit); - OptionalKeyRange optionalKeyRange; - - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - - optionalKeyRange = Move(serializedKeyRange); - } else { - optionalKeyRange = void_t(); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } - IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); - - OpenCursorParams params; - if (aKeysOnly) { - IndexOpenKeyCursorParams openParams; - openParams.objectStoreId() = objectStoreId; - openParams.indexId() = indexId; - openParams.optionalKeyRange() = Move(optionalKeyRange); - openParams.direction() = direction; - - params = Move(openParams); - } else { - IndexOpenCursorParams openParams; - openParams.objectStoreId() = objectStoreId; - openParams.indexId() = indexId; - openParams.optionalKeyRange() = Move(optionalKeyRange); - openParams.direction() = direction; - - params = Move(openParams); - } - - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundCursorChild* actor = - new BackgroundCursorChild(request, this, direction); - - mObjectStore->Transaction()->OpenCursor(actor, params); + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getAllKeys(%s, %lu)", + "IDBRequest[%llu] MT IDBIndex.getAllKeys()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + aLimit); return request.forget(); } already_AddRefed -IDBIndex::Count(JSContext* aCx, - JS::Handle aKey, - ErrorResult& aRv) +IDBIndex::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -455,52 +609,183 @@ IDBIndex::Count(JSContext* aCx, return nullptr; } - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (aRv.Failed()) { + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - IndexCountParams params; - params.objectStoreId() = mObjectStore->Id(); - params.indexId() = Id(); + nsRefPtr helper = + new CountHelper(transaction, request, this, aKeyRange); - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - params.optionalKeyRange() = serializedKeyRange; - } else { - params.optionalKeyRange() = void_t(); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - transaction->StartRequest(actor, params); - IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "count(%s)", - "IDBRequest[%llu] MT IDBObjectStore.count()", + "IDBRequest[%llu] MT IDBIndex.count()", request->GetSerialNumber(), - IDB_PROFILER_STRING(transaction->Database()), - IDB_PROFILER_STRING(transaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKey)); + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); return request.forget(); } -NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex) -NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex) +already_AddRefed +IDBIndex::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection, + ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + IDBCursor::Direction direction = + static_cast(aDirection); + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + nsRefPtr helper = + new OpenKeyCursorHelper(transaction, request, this, aKeyRange, direction); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "openKeyCursor(%s)", + "IDBRequest[%llu] MT IDBIndex.openKeyCursor()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + IDB_PROFILER_STRING(direction)); + + return request.forget(); +} + +nsresult +IDBIndex::OpenCursorInternal(IDBKeyRange* aKeyRange, + size_t aDirection, + IDBRequest** _retval) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; + } + + IDBCursor::Direction direction = + static_cast(aDirection); + + nsRefPtr request = GenerateRequest(this); + IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsRefPtr helper = + new OpenCursorHelper(transaction, request, this, aKeyRange, direction); + + nsresult rv = helper->DispatchToTransactionPool(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "openCursor(%s)", + "IDBRequest[%llu] MT IDBIndex.openCursor()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(ObjectStore()->Transaction()-> + Database()), + IDB_PROFILER_STRING(ObjectStore()->Transaction()), + IDB_PROFILER_STRING(ObjectStore()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + IDB_PROFILER_STRING(direction)); + + request.forget(_retval); + return NS_OK; +} + +nsresult +IDBIndex::OpenCursorFromChildProcess(IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + const Key& aObjectKey, + IDBCursor** _retval) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBCursor::Direction direction = + static_cast(aDirection); + + nsRefPtr cursor = + IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction, + Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + cursor.forget(_retval); + return NS_OK; +} + +nsresult +IDBIndex::OpenCursorFromChildProcess( + IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + const Key& aObjectKey, + const SerializedStructuredCloneReadInfo& aCloneInfo, + nsTArray& aBlobs, + IDBCursor** _retval) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) || + (aCloneInfo.dataLength && aCloneInfo.data), + "Inconsistent clone info!"); + + IDBCursor::Direction direction = + static_cast(aDirection); + + StructuredCloneReadInfo cloneInfo; + + if (!cloneInfo.SetFromSerialized(aCloneInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + cloneInfo.mFiles.SwapElements(aBlobs); + + nsRefPtr cursor = + IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction, + Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey, + Move(cloneInfo)); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!"); + + cursor.forget(_retval); + return NS_OK; +} NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex) @@ -527,12 +812,1765 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex) } NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex) + JSObject* IDBIndex::WrapObject(JSContext* aCx) { return IDBIndexBinding::Wrap(aCx, this); } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +void +IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, + ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!mCachedKeyPath.isUndefined()) { + JS::ExposeValueToActiveJS(mCachedKeyPath); + aResult.set(mCachedKeyPath); + return; + } + + aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (mCachedKeyPath.isGCThing()) { + mozilla::HoldJSObjects(this); + mRooted = true; + } + + JS::ExposeValueToActiveJS(mCachedKeyPath); + aResult.set(mCachedKeyPath); +} + +already_AddRefed +IDBIndex::Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + if (!keyRange) { + // Must specify a key or keyRange for getKey(). + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); + return nullptr; + } + + return GetInternal(keyRange, aRv); +} + +already_AddRefed +IDBIndex::GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + if (!keyRange) { + // Must specify a key or keyRange for get(). + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); + return nullptr; + } + + return GetKeyInternal(keyRange, aRv); +} + +already_AddRefed +IDBIndex::GetAll(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + uint32_t limit = UINT32_MAX; + if (aLimit.WasPassed() && aLimit.Value() > 0) { + limit = aLimit.Value(); + } + + return GetAllInternal(keyRange, limit, aRv); +} + +already_AddRefed +IDBIndex::GetAllKeys(JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + uint32_t limit = UINT32_MAX; + if (aLimit.WasPassed() && aLimit.Value() > 0) { + limit = aLimit.Value(); + } + + return GetAllKeysInternal(keyRange, limit, aRv); +} + +already_AddRefed +IDBIndex::OpenCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + nsRefPtr helper = + new OpenCursorHelper(transaction, request, this, keyRange, direction); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +IDBIndex::OpenKeyCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); + + return OpenKeyCursorInternal(keyRange, direction, aRv); +} + +already_AddRefed +IDBIndex::Count(JSContext* aCx, JS::Handle aKey, + ErrorResult& aRv) +{ + IDBTransaction* transaction = mObjectStore->Transaction(); + if (!transaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + return CountInternal(keyRange, aRv); +} + +void +IndexHelper::ReleaseMainThreadObjects() +{ + mIndex = nullptr; + AsyncConnectionHelper::ReleaseMainThreadObjects(); +} + +nsresult +IndexHelper::Dispatch(nsIEventTarget* aDatabaseThread) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("IndexHelper", "Dispatch", + js::ProfileEntry::Category::STORAGE); + + if (IndexedDatabaseManager::IsMainProcess()) { + return AsyncConnectionHelper::Dispatch(aDatabaseThread); + } + + // If we've been invalidated then there's no point sending anything to the + // parent process. + if (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IndexedDBIndexChild* indexActor = mIndex->GetActorChild(); + NS_ASSERTION(indexActor, "Must have an actor here!"); + + IndexRequestParams params; + nsresult rv = PackArgumentsForParentProcess(params); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NoDispatchEventTarget target; + rv = AsyncConnectionHelper::Dispatch(&target); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mActor = new IndexedDBIndexRequestChild(this, mIndex, params.type()); + indexActor->SendPIndexedDBRequestConstructor(mActor, params); + + return NS_OK; +} + +nsresult +GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "Must have a key range here!"); + + PROFILER_LABEL("GetKeyHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCString indexTable; + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("unique_index_data"); + } + else { + indexTable.AssignLiteral("index_data"); + } + + nsCString keyRangeClause; + mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); + + NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); + + nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + + indexTable + + NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + + keyRangeClause + + NS_LITERAL_CSTRING(" LIMIT 1"); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (hasResult) { + rv = mKey.SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +GetKeyHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + return mKey.ToJSVal(aCx, aVal); +} + +void +GetKeyHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + IndexHelper::ReleaseMainThreadObjects(); +} + +nsresult +GetKeyHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "This should never be null!"); + + PROFILER_MAIN_THREAD_LABEL("GetKeyHelper", "PackArgumentsForParentProcess", + js::ProfileEntry::Category::STORAGE); + + GetKeyParams params; + + mKeyRange->ToSerializedKeyRange(params.keyRange()); + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetKeyHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("GetKeyHelper", "SendResponseToChildProcess", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + GetKeyResponse getKeyResponse; + getKeyResponse.key() = mKey; + response = getKeyResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetKeyHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetKeyResponse, + "Bad response type!"); + + mKey = aResponseValue.get_GetKeyResponse().key(); + return NS_OK; +} + +nsresult +GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "Must have a key range here!"); + + PROFILER_LABEL("GetHelper", "DoDatabaseWork [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsCString indexTable; + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("unique_index_data"); + } + else { + indexTable.AssignLiteral("index_data"); + } + + nsCString keyRangeClause; + mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); + + NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); + + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " + "INNER JOIN ") + indexTable + + NS_LITERAL_CSTRING(" AS index_table ON object_data.id = ") + + NS_LITERAL_CSTRING("index_table.object_data_id WHERE " + "index_id = :index_id") + + keyRangeClause + + NS_LITERAL_CSTRING(" LIMIT 1"); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (hasResult) { + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, + mDatabase, mCloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +GetHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal); + + mCloneReadInfo.mCloneBuffer.clear(); + + NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); + return NS_OK; +} + +void +GetHelper::ReleaseMainThreadObjects() +{ + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + GetKeyHelper::ReleaseMainThreadObjects(); +} + +nsresult +GetHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "This should never be null!"); + + PROFILER_MAIN_THREAD_LABEL("GetHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + GetParams params; + + mKeyRange->ToSerializedKeyRange(params.keyRange()); + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("GetHelper", "SendResponseToChildProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + InfallibleTArray blobsParent; + + if (NS_SUCCEEDED(aResultCode)) { + IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + const nsTArray& files = mCloneReadInfo.mFiles; + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobsParent); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobActors failed!"); + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + GetResponse getResponse; + getResponse.cloneInfo() = mCloneReadInfo; + getResponse.blobsParent().SwapElements(blobsParent); + response = getResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse, + "Bad response type!"); + + const GetResponse& getResponse = aResponseValue.get_GetResponse(); + const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo(); + + NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || + (cloneInfo.dataLength && cloneInfo.data), + "Inconsistent clone info!"); + + if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(), + mCloneReadInfo.mFiles); + return NS_OK; +} + +nsresult +GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + PROFILER_LABEL("GetAllKeysHelper", "DoDatabaseWork [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsCString tableName; + if (mIndex->IsUnique()) { + tableName.AssignLiteral("unique_index_data"); + } + else { + tableName.AssignLiteral("index_data"); + } + + nsCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); + } + + nsCString limitClause; + if (mLimit != UINT32_MAX) { + limitClause = NS_LITERAL_CSTRING(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + + tableName + + NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + + keyRangeClause + limitClause; + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + mKeys.SetCapacity(std::min(50, mLimit)); + + bool hasResult; + while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + if (mKeys.Capacity() == mKeys.Length()) { + mKeys.SetCapacity(mKeys.Capacity() * 2); + } + + Key* key = mKeys.AppendElement(); + NS_ASSERTION(key, "This shouldn't fail!"); + + rv = key->SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +GetAllKeysHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mKeys.Length() <= mLimit); + + PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "GetSuccessResult [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsTArray keys; + mKeys.SwapElements(keys); + + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (!array) { + IDB_WARNING("Failed to make array!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!keys.IsEmpty()) { + if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) { + IDB_WARNING("Failed to set array length!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0, count = keys.Length(); index < count; index++) { + const Key& key = keys[index]; + NS_ASSERTION(!key.IsUnset(), "Bad key!"); + + JS::Rooted value(aCx); + nsresult rv = key.ToJSVal(aCx, &value); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get jsval for key!"); + return rv; + } + + if (!JS_SetElement(aCx, array, index, value)) { + IDB_WARNING("Failed to set array element!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aVal.setObject(*array); + return NS_OK; +} + +nsresult +GetAllKeysHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); + + PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + GetAllKeysParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.limit() = mLimit; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "SendResponseToChildProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + GetAllKeysResponse getAllKeysResponse; + getAllKeysResponse.keys().AppendElements(mKeys); + response = getAllKeysResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetAllKeysHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse); + + mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys()); + return NS_OK; +} + +nsresult +GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("GetAllHelper", "DoDatabaseWork [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsCString indexTable; + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("unique_index_data"); + } + else { + indexTable.AssignLiteral("index_data"); + } + + nsCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); + } + + nsCString limitClause; + if (mLimit != UINT32_MAX) { + limitClause = NS_LITERAL_CSTRING(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " + "INNER JOIN ") + indexTable + + NS_LITERAL_CSTRING(" AS index_table ON object_data.id = " + "index_table.object_data_id " + "WHERE index_id = :index_id") + + keyRangeClause + limitClause; + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + mCloneReadInfos.SetCapacity(50); + + bool hasResult; + while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) { + mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2); + } + + StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement(); + NS_ASSERTION(readInfo, "This shouldn't fail!"); + + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, + mDatabase, *readInfo); + NS_ENSURE_SUCCESS(rv, rv); + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +GetAllHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!"); + + nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal); + + NS_ASSERTION(mCloneReadInfos.IsEmpty(), + "Should have cleared in ConvertToArrayAndCleanup"); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +GetAllHelper::ReleaseMainThreadObjects() +{ + for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); + } + GetKeyHelper::ReleaseMainThreadObjects(); +} + +nsresult +GetAllHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + GetAllParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.limit() = mLimit; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetAllHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "SendResponseToChildProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + GetAllResponse getAllResponse; + + if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) { + IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + uint32_t length = mCloneReadInfos.Length(); + + InfallibleTArray& infos = + getAllResponse.cloneInfos(); + infos.SetCapacity(length); + + InfallibleTArray& blobArrays = getAllResponse.blobs(); + blobArrays.SetCapacity(length); + + for (uint32_t index = 0; + NS_SUCCEEDED(aResultCode) && index < length; + index++) { + const StructuredCloneReadInfo& clone = mCloneReadInfos[index]; + + // Append the structured clone data. + SerializedStructuredCloneReadInfo* info = infos.AppendElement(); + *info = clone; + + const nsTArray& files = clone.mFiles; + + // Now take care of the files. + BlobArray* blobArray = blobArrays.AppendElement(); + + InfallibleTArray& blobs = blobArray->blobsParent(); + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobs); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobsToActors failed!"); + break; + } + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + response = getAllResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetAllHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse, + "Bad response type!"); + + const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse(); + const InfallibleTArray& cloneInfos = + getAllResponse.cloneInfos(); + const InfallibleTArray& blobArrays = getAllResponse.blobs(); + + mCloneReadInfos.SetCapacity(cloneInfos.Length()); + + for (uint32_t index = 0; index < cloneInfos.Length(); index++) { + const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index]; + const InfallibleTArray& blobs = blobArrays[index].blobsChild(); + + StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement(); + if (!destInfo->SetFromSerialized(srcInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles); + } + + return NS_OK; +} + +nsresult +OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aConnection, "Passed a null connection!"); + + PROFILER_LABEL("OpenKeyCursorHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCString table; + if (mIndex->IsUnique()) { + table.AssignLiteral("unique_index_data"); + } + else { + table.AssignLiteral("index_data"); + } + + NS_NAMED_LITERAL_CSTRING(value, "value"); + + nsCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(value, keyRangeClause); + } + + nsAutoCString directionClause(" ORDER BY value "); + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause += NS_LITERAL_CSTRING("ASC, object_data_key ASC"); + break; + + case IDBCursor::PREV: + directionClause += NS_LITERAL_CSTRING("DESC, object_data_key DESC"); + break; + + case IDBCursor::PREV_UNIQUE: + directionClause += NS_LITERAL_CSTRING("DESC, object_data_key ASC"); + break; + + default: + NS_NOTREACHED("Unknown direction!"); + } + nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key " + "FROM ") + table + + NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + + keyRangeClause + directionClause + + NS_LITERAL_CSTRING(" LIMIT 1"); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement(firstQuery); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!hasResult) { + mKey.Unset(); + return NS_OK; + } + + rv = mKey.SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mObjectKey.SetFromStatement(stmt, 1); + NS_ENSURE_SUCCESS(rv, rv); + + // Now we need to make the query to get the next match. + nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT value, object_data_key" + " FROM ") + table + + NS_LITERAL_CSTRING(" WHERE index_id = :id"); + + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mDirection) { + case IDBCursor::NEXT: + if (mKeyRange && !mKeyRange->Upper().IsUnset()) { + AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), + queryStart); + mRangeKey = mKeyRange->Upper(); + } + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value >= :current_key AND " + "( value > :current_key OR " + " object_data_key > :object_key )") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value >= :current_key ") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + break; + + case IDBCursor::NEXT_UNIQUE: + if (mKeyRange && !mKeyRange->Upper().IsUnset()) { + AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), + queryStart); + mRangeKey = mKeyRange->Upper(); + } + mContinueQuery = + queryStart + NS_LITERAL_CSTRING(" AND value > :current_key") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = + queryStart + NS_LITERAL_CSTRING(" AND value >= :current_key") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + break; + + case IDBCursor::PREV: + if (mKeyRange && !mKeyRange->Lower().IsUnset()) { + AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), + queryStart); + mRangeKey = mKeyRange->Lower(); + } + + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value <= :current_key AND " + "( value < :current_key OR " + " object_data_key < :object_key )") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value <= :current_key ") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + break; + + case IDBCursor::PREV_UNIQUE: + if (mKeyRange && !mKeyRange->Lower().IsUnset()) { + AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), + queryStart); + mRangeKey = mKeyRange->Lower(); + } + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value < :current_key") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value <= :current_key") + + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + break; + + default: + NS_NOTREACHED("Unknown direction type!"); + } + + return NS_OK; +} + +nsresult +OpenKeyCursorHelper::EnsureCursor() +{ + if (mCursor || mKey.IsUnset()) { + return NS_OK; + } + + nsRefPtr cursor = + IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey, + mContinueQuery, mContinueToQuery, mKey, mObjectKey); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mCursor.swap(cursor); + return NS_OK; +} + +nsresult +OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + nsresult rv = EnsureCursor(); + NS_ENSURE_SUCCESS(rv, rv); + + if (mCursor) { + rv = WrapNative(aCx, mCursor, aVal); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + aVal.setUndefined(); + } + + return NS_OK; +} + +void +OpenKeyCursorHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + mCursor = nullptr; + IndexHelper::ReleaseMainThreadObjects(); +} + +nsresult +OpenKeyCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + OpenKeyCursorParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.direction() = mDirection; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "SendResponseToChildProcess", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + if (NS_SUCCEEDED(aResultCode)) { + nsresult rv = EnsureCursor(); + if (NS_FAILED(rv)) { + NS_WARNING("EnsureCursor failed!"); + aResultCode = rv; + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + OpenCursorResponse openCursorResponse; + + if (!mCursor) { + openCursorResponse = mozilla::void_t(); + } + else { + IndexedDBIndexParent* indexActor = mIndex->GetActorParent(); + NS_ASSERTION(indexActor, "Must have an actor here!"); + + IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); + NS_ASSERTION(requestActor, "Must have an actor here!"); + + IndexCursorConstructorParams params; + params.requestParent() = requestActor; + params.direction() = mDirection; + params.key() = mKey; + params.objectKey() = mObjectKey; + params.optionalCloneInfo() = mozilla::void_t(); + + if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) { + return Error; + } + } + + response = openCursorResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +OpenKeyCursorHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse, + "Bad response type!"); + NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() == + OpenCursorResponse::Tvoid_t || + aResponseValue.get_OpenCursorResponse().type() == + OpenCursorResponse::TPIndexedDBCursorChild, + "Bad response union type!"); + NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); + + const OpenCursorResponse& response = + aResponseValue.get_OpenCursorResponse(); + + switch (response.type()) { + case OpenCursorResponse::Tvoid_t: + break; + + case OpenCursorResponse::TPIndexedDBCursorChild: { + IndexedDBCursorChild* actor = + static_cast( + response.get_PIndexedDBCursorChild()); + + mCursor = actor->ForgetStrongCursor(); + NS_ASSERTION(mCursor, "This should never be null!"); + + } break; + + default: + MOZ_CRASH(); + } + + return NS_OK; +} + +nsresult +OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aConnection, "Passed a null connection!"); + + PROFILER_LABEL("OpenCursorHelper", "DoDatabaseWork [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsCString indexTable; + if (mIndex->IsUnique()) { + indexTable.AssignLiteral("unique_index_data"); + } + else { + indexTable.AssignLiteral("index_data"); + } + + NS_NAMED_LITERAL_CSTRING(value, "index_table.value"); + + nsCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(value, keyRangeClause); + } + + nsAutoCString directionClause(" ORDER BY index_table.value "); + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause += + NS_LITERAL_CSTRING("ASC, index_table.object_data_key ASC"); + break; + + case IDBCursor::PREV: + directionClause += + NS_LITERAL_CSTRING("DESC, index_table.object_data_key DESC"); + break; + + case IDBCursor::PREV_UNIQUE: + directionClause += + NS_LITERAL_CSTRING("DESC, index_table.object_data_key ASC"); + break; + + default: + NS_NOTREACHED("Unknown direction!"); + } + + nsCString firstQuery = + NS_LITERAL_CSTRING("SELECT index_table.value, " + "index_table.object_data_key, object_data.data, " + "object_data.file_ids FROM ") + + indexTable + + NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " + "index_table.object_data_id = object_data.id " + "WHERE index_table.index_id = :id") + + keyRangeClause + directionClause + + NS_LITERAL_CSTRING(" LIMIT 1"); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement(firstQuery); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!hasResult) { + mKey.Unset(); + return NS_OK; + } + + rv = mKey.SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mObjectKey.SetFromStatement(stmt, 1); + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3, + mDatabase, mCloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + // Now we need to make the query to get the next match. + nsAutoCString queryStart = + NS_LITERAL_CSTRING("SELECT index_table.value, " + "index_table.object_data_key, object_data.data, " + "object_data.file_ids FROM ") + + indexTable + + NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " + "index_table.object_data_id = object_data.id " + "WHERE index_table.index_id = :id"); + + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + NS_NAMED_LITERAL_CSTRING(limit, " LIMIT "); + + switch (mDirection) { + case IDBCursor::NEXT: + if (mKeyRange && !mKeyRange->Upper().IsUnset()) { + AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), + queryStart); + mRangeKey = mKeyRange->Upper(); + } + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value >= :current_key AND " + "( index_table.value > :current_key OR " + " index_table.object_data_key > :object_key ) ") + + directionClause + limit; + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + + directionClause + limit; + break; + + case IDBCursor::NEXT_UNIQUE: + if (mKeyRange && !mKeyRange->Upper().IsUnset()) { + AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), + queryStart); + mRangeKey = mKeyRange->Upper(); + } + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value > :current_key") + + directionClause + limit; + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + + directionClause + limit; + break; + + case IDBCursor::PREV: + if (mKeyRange && !mKeyRange->Lower().IsUnset()) { + AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), + queryStart); + mRangeKey = mKeyRange->Lower(); + } + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value <= :current_key AND " + "( index_table.value < :current_key OR " + " index_table.object_data_key < :object_key ) ") + + directionClause + limit; + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + + directionClause + limit; + break; + + case IDBCursor::PREV_UNIQUE: + if (mKeyRange && !mKeyRange->Lower().IsUnset()) { + AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), + queryStart); + mRangeKey = mKeyRange->Lower(); + } + mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value < :current_key") + + directionClause + limit; + mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + + directionClause + limit; + break; + + default: + NS_NOTREACHED("Unknown direction type!"); + } + + return NS_OK; +} + +nsresult +OpenCursorHelper::EnsureCursor() +{ + if (mCursor || mKey.IsUnset()) { + return NS_OK; + } + + mSerializedCloneReadInfo = mCloneReadInfo; + + NS_ASSERTION(mSerializedCloneReadInfo.data && + mSerializedCloneReadInfo.dataLength, + "Shouldn't be possible!"); + + nsRefPtr cursor = + IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey, + mContinueQuery, mContinueToQuery, mKey, mObjectKey, + Move(mCloneReadInfo)); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!"); + + mCursor.swap(cursor); + return NS_OK; +} + +void +OpenCursorHelper::ReleaseMainThreadObjects() +{ + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + + // These don't need to be released on the main thread but they're only valid + // as long as mCursor is set. + mSerializedCloneReadInfo.data = nullptr; + mSerializedCloneReadInfo.dataLength = 0; + + OpenKeyCursorHelper::ReleaseMainThreadObjects(); +} + +nsresult +OpenCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + OpenCursorParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.direction() = mDirection; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); + + PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "SendResponseToChildProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + InfallibleTArray blobsParent; + + if (NS_SUCCEEDED(aResultCode)) { + IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + const nsTArray& files = mCloneReadInfo.mFiles; + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobsParent); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobsToActors failed!"); + } + } + + if (NS_SUCCEEDED(aResultCode)) { + nsresult rv = EnsureCursor(); + if (NS_FAILED(rv)) { + NS_WARNING("EnsureCursor failed!"); + aResultCode = rv; + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + OpenCursorResponse openCursorResponse; + + if (!mCursor) { + openCursorResponse = mozilla::void_t(); + } + else { + IndexedDBIndexParent* indexActor = mIndex->GetActorParent(); + NS_ASSERTION(indexActor, "Must have an actor here!"); + + IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); + NS_ASSERTION(requestActor, "Must have an actor here!"); + + NS_ASSERTION(mSerializedCloneReadInfo.data && + mSerializedCloneReadInfo.dataLength, + "Shouldn't be possible!"); + + IndexCursorConstructorParams params; + params.requestParent() = requestActor; + params.direction() = mDirection; + params.key() = mKey; + params.objectKey() = mObjectKey; + params.optionalCloneInfo() = mSerializedCloneReadInfo; + params.blobsParent().SwapElements(blobsParent); + + if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) { + return Error; + } + } + + response = openCursorResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("CountHelper", "DoDatabaseWork [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsCString table; + if (mIndex->IsUnique()) { + table.AssignLiteral("unique_index_data"); + } + else { + table.AssignLiteral("index_data"); + } + + NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); + NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); + NS_NAMED_LITERAL_CSTRING(value, "value"); + + nsAutoCString keyRangeClause; + if (mKeyRange) { + if (!mKeyRange->Lower().IsUnset()) { + AppendConditionClause(value, lowerKeyName, false, + !mKeyRange->IsLowerOpen(), keyRangeClause); + } + if (!mKeyRange->Upper().IsUnset()) { + AppendConditionClause(value, upperKeyName, true, + !mKeyRange->IsUpperOpen(), keyRangeClause); + } + } + + nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM ") + table + + NS_LITERAL_CSTRING(" WHERE index_id = :id") + + keyRangeClause; + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + if (!mKeyRange->Lower().IsUnset()) { + rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + if (!mKeyRange->Upper().IsUnset()) { + rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mCount = stmt->AsInt64(0); + return NS_OK; +} + +nsresult +CountHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + aVal.setNumber(static_cast(mCount)); + return NS_OK; +} + +void +CountHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + IndexHelper::ReleaseMainThreadObjects(); +} + +nsresult +CountHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("CountHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + CountParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +CountHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("CountHelper", "SendResponseToChildProcess [IDBIndex.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + CountResponse countResponse = mCount; + response = countResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +CountHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse, + "Bad response type!"); + + mCount = aResponseValue.get_CountResponse().count(); + return NS_OK; +} diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index 8b40e1fd16fa..806f4ec93e9a 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -7,207 +7,245 @@ #ifndef mozilla_dom_indexeddb_idbindex_h__ #define mozilla_dom_indexeddb_idbindex_h__ -#include "js/RootingAPI.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + #include "mozilla/Attributes.h" #include "mozilla/dom/IDBCursorBinding.h" -#include "nsAutoPtr.h" +#include "mozilla/ErrorResult.h" #include "nsCycleCollectionParticipant.h" -#include "nsISupports.h" -#include "nsTArrayForwardDeclare.h" #include "nsWrapperCache.h" +#include "mozilla/dom/indexedDB/IDBObjectStore.h" +#include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/indexedDB/KeyPath.h" + +class nsIScriptContext; class nsPIDOMWindow; -namespace mozilla { - -class ErrorResult; - -namespace dom { - -template class Sequence; - -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE +class AsyncConnectionHelper; +class IDBCursor; +class IDBKeyRange; class IDBObjectStore; class IDBRequest; -class IndexMetadata; +class IndexedDBIndexChild; +class IndexedDBIndexParent; class Key; -class KeyPath; -class IDBIndex MOZ_FINAL - : public nsISupports - , public nsWrapperCache +struct IndexInfo; + +class IDBIndex MOZ_FINAL : public nsISupports, + public nsWrapperCache { - nsRefPtr mObjectStore; - - JS::Heap mCachedKeyPath; - - // This normally points to the IndexMetadata owned by the parent IDBDatabase - // object. However, if this index is part of a versionchange transaction and - // it gets deleted then the metadata is copied into mDeletedMetadata and - // mMetadata is set to point at mDeletedMetadata. - const IndexMetadata* mMetadata; - nsAutoPtr mDeletedMetadata; - - const int64_t mId; - bool mRooted; - public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBIndex) static already_AddRefed - Create(IDBObjectStore* aObjectStore, const IndexMetadata& aMetadata); + Create(IDBObjectStore* aObjectStore, + const IndexInfo* aIndexInfo, + bool aCreating); - int64_t - Id() const + IDBObjectStore* ObjectStore() { - AssertIsOnOwningThread(); - - return mId; - } - - const nsString& - Name() const; - - bool - Unique() const; - - bool - MultiEntry() const; - - const KeyPath& - GetKeyPath() const; - - IDBObjectStore* - ObjectStore() const - { - AssertIsOnOwningThread(); return mObjectStore; } - nsPIDOMWindow* - GetParentObject() const; - - void - GetName(nsString& aName) const + const int64_t Id() const { - aName = Name(); + return mId; + } + + const nsString& Name() const + { + return mName; + } + + bool IsUnique() const + { + return mUnique; + } + + bool IsMultiEntry() const + { + return mMultiEntry; + } + + const KeyPath& GetKeyPath() const + { + return mKeyPath; } void - GetKeyPath(JSContext* aCx, - JS::MutableHandle aResult, - ErrorResult& aRv); - - already_AddRefed - OpenCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv) + SetActor(IndexedDBIndexChild* aActorChild) { - AssertIsOnOwningThread(); - - return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection, - aRv); - } - - already_AddRefed - OpenKeyCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, - aRv); - } - - already_AddRefed - Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv); - } - - already_AddRefed - GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv); - } - - already_AddRefed - Count(JSContext* aCx, JS::Handle aKey, - ErrorResult& aRv); - - already_AddRefed - GetAll(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv); - } - - already_AddRefed - GetAllKeys(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv); + NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); + mActorChild = aActorChild; } void - RefreshMetadata(bool aMayDelete); + SetActor(IndexedDBIndexParent* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, + "Shouldn't have more than one!"); + mActorParent = aActorParent; + } - void - NoteDeletion(); + IndexedDBIndexChild* + GetActorChild() const + { + return mActorChild; + } - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif + IndexedDBIndexParent* + GetActorParent() const + { + return mActorParent; + } + + already_AddRefed + GetInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv); + + already_AddRefed + GetKeyInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv); + + already_AddRefed + GetAllInternal(IDBKeyRange* aKeyRange, + uint32_t aLimit, + ErrorResult& aRv); + + already_AddRefed + GetAllKeysInternal(IDBKeyRange* aKeyRange, + uint32_t aLimit, + ErrorResult& aRv); + + already_AddRefed + CountInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv); + + nsresult OpenCursorFromChildProcess( + IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + const Key& aObjectKey, + IDBCursor** _retval); + + already_AddRefed + OpenKeyCursorInternal(IDBKeyRange* aKeyRange, + size_t aDirection, + ErrorResult& aRv); + + nsresult OpenCursorInternal(IDBKeyRange* aKeyRange, + size_t aDirection, + IDBRequest** _retval); + + nsresult OpenCursorFromChildProcess( + IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + const Key& aObjectKey, + const SerializedStructuredCloneReadInfo& aCloneInfo, + nsTArray& aBlobs, + IDBCursor** _retval); // nsWrapperCache virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; -private: - IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata); + // WebIDL + IDBObjectStore* + GetParentObject() const + { + return mObjectStore; + } + void + GetName(nsString& aName) const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + aName.Assign(mName); + } + + IDBObjectStore* + ObjectStore() const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mObjectStore; + } + + void + GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, + ErrorResult& aRv); + + bool + MultiEntry() const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mMultiEntry; + } + + bool + Unique() const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mUnique; + } + + already_AddRefed + OpenCursor(JSContext* aCx, JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv); + + already_AddRefed + OpenKeyCursor(JSContext* aCx, JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv); + + already_AddRefed + Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); + + already_AddRefed + GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); + + already_AddRefed + Count(JSContext* aCx, JS::Handle aKey, + ErrorResult& aRv); + + void + GetStoreName(nsString& aStoreName) const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + mObjectStore->GetName(aStoreName); + } + + already_AddRefed + GetAll(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv); + + already_AddRefed + GetAllKeys(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv); + +private: + IDBIndex(); ~IDBIndex(); - already_AddRefed - GetInternal(bool aKeyOnly, - JSContext* aCx, - JS::Handle aKey, - ErrorResult& aRv); + nsRefPtr mObjectStore; - already_AddRefed - GetAllInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, - ErrorResult& aRv); + int64_t mId; + nsString mName; + KeyPath mKeyPath; + JS::Heap mCachedKeyPath; - already_AddRefed - OpenCursorInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv); + IndexedDBIndexChild* mActorChild; + IndexedDBIndexParent* mActorParent; + + bool mUnique; + bool mMultiEntry; + bool mRooted; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbindex_h__ diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp index f7b2ed354891..7c69077a01a6 100644 --- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -4,21 +4,30 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "base/basictypes.h" + #include "IDBKeyRange.h" -#include "Key.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/IDBKeyRangeBinding.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "nsIXPConnect.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "nsJSUtils.h" +#include "nsThreadUtils.h" +#include "nsContentUtils.h" +#include "nsDOMClassInfoID.h" +#include "Key.h" + +#include "mozilla/dom/IDBKeyRangeBinding.h" +#include "mozilla/dom/indexedDB/PIndexedDBIndex.h" +#include "mozilla/dom/indexedDB/PIndexedDBObjectStore.h" + +using namespace mozilla; +using namespace mozilla::dom; +USING_INDEXEDDB_NAMESPACE +using namespace mozilla::dom::indexedDB::ipc; namespace { -nsresult +inline nsresult GetKeyFromJSVal(JSContext* aCx, JS::Handle aVal, Key& aKey, @@ -26,7 +35,8 @@ GetKeyFromJSVal(JSContext* aCx, { nsresult rv = aKey.SetFromJSVal(aCx, aVal); if (NS_FAILED(rv)) { - MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB); + NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB, + "Bad error code!"); return rv; } @@ -39,42 +49,6 @@ GetKeyFromJSVal(JSContext* aCx, } // anonymous namespace -IDBKeyRange::IDBKeyRange(nsISupports* aGlobal, - bool aLowerOpen, - bool aUpperOpen, - bool aIsOnly) - : mGlobal(aGlobal) - , mCachedLowerVal(JSVAL_VOID) - , mCachedUpperVal(JSVAL_VOID) - , mLowerOpen(aLowerOpen) - , mUpperOpen(aUpperOpen) - , mIsOnly(aIsOnly) - , mHaveCachedLowerVal(false) - , mHaveCachedUpperVal(false) - , mRooted(false) -{ -#ifdef DEBUG - mOwningThread = PR_GetCurrentThread(); -#endif - AssertIsOnOwningThread(); -} - -IDBKeyRange::~IDBKeyRange() -{ - DropJSObjects(); -} - -#ifdef DEBUG - -void -IDBKeyRange::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mOwningThread); - MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); -} - -#endif // DEBUG - // static nsresult IDBKeyRange::FromJSVal(JSContext* aCx, @@ -113,8 +87,9 @@ IDBKeyRange::FromJSVal(JSContext* aCx, } // static +template already_AddRefed -IDBKeyRange::FromSerialized(const SerializedKeyRange& aKeyRange) +IDBKeyRange::FromSerializedKeyRange(const T& aKeyRange) { nsRefPtr keyRange = new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(), @@ -126,11 +101,12 @@ IDBKeyRange::FromSerialized(const SerializedKeyRange& aKeyRange) return keyRange.forget(); } +template void -IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const +IDBKeyRange::ToSerializedKeyRange(T& aKeyRange) { - aKeyRange.lowerOpen() = LowerOpen(); - aKeyRange.upperOpen() = UpperOpen(); + aKeyRange.lowerOpen() = IsLowerOpen(); + aKeyRange.upperOpen() = IsUpperOpen(); aKeyRange.isOnly() = IsOnly(); aKeyRange.lower() = Lower(); @@ -139,76 +115,6 @@ IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const } } -void -IDBKeyRange::GetBindingClause(const nsACString& aKeyColumnName, - nsACString& _retval) const -{ - NS_NAMED_LITERAL_CSTRING(andStr, " AND "); - NS_NAMED_LITERAL_CSTRING(spacecolon, " :"); - NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); - - if (IsOnly()) { - // Both keys are set and they're equal. - _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + - spacecolon + lowerKey; - return; - } - - nsAutoCString clause; - - if (!Lower().IsUnset()) { - // Lower key is set. - clause.Append(andStr + aKeyColumnName); - clause.AppendLiteral(" >"); - if (!LowerOpen()) { - clause.Append('='); - } - clause.Append(spacecolon + lowerKey); - } - - if (!Upper().IsUnset()) { - // Upper key is set. - clause.Append(andStr + aKeyColumnName); - clause.AppendLiteral(" <"); - if (!UpperOpen()) { - clause.Append('='); - } - clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key")); - } - - _retval = clause; -} - -nsresult -IDBKeyRange::BindToStatement(mozIStorageStatement* aStatement) const -{ - MOZ_ASSERT(aStatement); - - NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); - - if (IsOnly()) { - return Lower().BindToStatement(aStatement, lowerKey); - } - - nsresult rv; - - if (!Lower().IsUnset()) { - rv = Lower().BindToStatement(aStatement, lowerKey); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - if (!Upper().IsUnset()) { - rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } - - return NS_OK; -} - NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange) @@ -239,14 +145,19 @@ IDBKeyRange::DropJSObjects() if (!mRooted) { return; } - mCachedLowerVal.setUndefined(); - mCachedUpperVal.setUndefined(); + mCachedLowerVal = JS::UndefinedValue(); + mCachedUpperVal = JS::UndefinedValue(); mHaveCachedLowerVal = false; mHaveCachedUpperVal = false; mRooted = false; mozilla::DropJSObjects(this); } +IDBKeyRange::~IDBKeyRange() +{ + DropJSObjects(); +} + JSObject* IDBKeyRange::WrapObject(JSContext* aCx) { @@ -257,7 +168,7 @@ void IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); if (!mHaveCachedLowerVal) { if (!mRooted) { @@ -281,7 +192,7 @@ void IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); if (!mHaveCachedUpperVal) { if (!mRooted) { @@ -304,9 +215,10 @@ IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle aResult, // static already_AddRefed IDBKeyRange::Only(const GlobalObject& aGlobal, - JS::Handle aValue, - ErrorResult& aRv) + JS::Handle aValue, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true); @@ -321,10 +233,11 @@ IDBKeyRange::Only(const GlobalObject& aGlobal, // static already_AddRefed IDBKeyRange::LowerBound(const GlobalObject& aGlobal, - JS::Handle aValue, - bool aOpen, + JS::Handle aValue, bool aOpen, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false); @@ -339,10 +252,11 @@ IDBKeyRange::LowerBound(const GlobalObject& aGlobal, // static already_AddRefed IDBKeyRange::UpperBound(const GlobalObject& aGlobal, - JS::Handle aValue, - bool aOpen, + JS::Handle aValue, bool aOpen, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false); @@ -357,12 +271,11 @@ IDBKeyRange::UpperBound(const GlobalObject& aGlobal, // static already_AddRefed IDBKeyRange::Bound(const GlobalObject& aGlobal, - JS::Handle aLower, - JS::Handle aUpper, - bool aLowerOpen, - bool aUpperOpen, - ErrorResult& aRv) + JS::Handle aLower, JS::Handle aUpper, + bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false); @@ -385,6 +298,9 @@ IDBKeyRange::Bound(const GlobalObject& aGlobal, return keyRange.forget(); } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +// Explicitly instantiate for all our key range types... Grumble. +template already_AddRefed +IDBKeyRange::FromSerializedKeyRange (const KeyRange& aKeyRange); + +template void +IDBKeyRange::ToSerializedKeyRange (KeyRange& aKeyRange); diff --git a/dom/indexedDB/IDBKeyRange.h b/dom/indexedDB/IDBKeyRange.h index ba307594d351..85ade2510917 100644 --- a/dom/indexedDB/IDBKeyRange.h +++ b/dom/indexedDB/IDBKeyRange.h @@ -7,137 +7,144 @@ #ifndef mozilla_dom_indexeddb_idbkeyrange_h__ #define mozilla_dom_indexeddb_idbkeyrange_h__ -#include "js/RootingAPI.h" -#include "js/Value.h" -#include "mozilla/Attributes.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" #include "mozilla/dom/indexedDB/Key.h" -#include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" + #include "nsISupports.h" -#include "nsString.h" + +#include "mozilla/ErrorResult.h" +#include "nsCycleCollectionParticipant.h" class mozIStorageStatement; -struct PRThread; namespace mozilla { - -class ErrorResult; - namespace dom { - class GlobalObject; +} // namespace dom +} // namespace mozilla -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE -class SerializedKeyRange; +namespace ipc { +class KeyRange; +} // namespace ipc -class IDBKeyRange MOZ_FINAL - : public nsISupports +class IDBKeyRange MOZ_FINAL : public nsISupports { - nsCOMPtr mGlobal; - Key mLower; - Key mUpper; - JS::Heap mCachedLowerVal; - JS::Heap mCachedUpperVal; - - const bool mLowerOpen : 1; - const bool mUpperOpen : 1; - const bool mIsOnly : 1; - bool mHaveCachedLowerVal : 1; - bool mHaveCachedUpperVal : 1; - bool mRooted : 1; - -#ifdef DEBUG - PRThread* mOwningThread; -#endif - public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange) - static nsresult - FromJSVal(JSContext* aCx, - JS::Handle aVal, - IDBKeyRange** aKeyRange); + static nsresult FromJSVal(JSContext* aCx, + JS::Handle aVal, + IDBKeyRange** aKeyRange); + template static already_AddRefed - FromSerialized(const SerializedKeyRange& aKeyRange); + FromSerializedKeyRange(const T& aKeyRange); - static already_AddRefed - Only(const GlobalObject& aGlobal, - JS::Handle aValue, - ErrorResult& aRv); - - static already_AddRefed - LowerBound(const GlobalObject& aGlobal, - JS::Handle aValue, - bool aOpen, - ErrorResult& aRv); - - static already_AddRefed - UpperBound(const GlobalObject& aGlobal, - JS::Handle aValue, - bool aOpen, - ErrorResult& aRv); - - static already_AddRefed - Bound(const GlobalObject& aGlobal, - JS::Handle aLower, - JS::Handle aUpper, - bool aLowerOpen, - bool aUpperOpen, - ErrorResult& aRv); - - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - void - ToSerialized(SerializedKeyRange& aKeyRange) const; - - const Key& - Lower() const + const Key& Lower() const { return mLower; } - Key& - Lower() + Key& Lower() { return mLower; } - const Key& - Upper() const + const Key& Upper() const { return mIsOnly ? mLower : mUpper; } - Key& - Upper() + Key& Upper() { return mIsOnly ? mLower : mUpper; } - bool - IsOnly() const + // TODO: Remove these in favour of LowerOpen() / UpperOpen(), bug 900578. + bool IsLowerOpen() const + { + return mLowerOpen; + } + + bool IsUpperOpen() const + { + return mUpperOpen; + } + + bool IsOnly() const { return mIsOnly; } - void - GetBindingClause(const nsACString& aKeyColumnName, - nsACString& _retval) const; + void GetBindingClause(const nsACString& aKeyColumnName, + nsACString& _retval) const + { + NS_NAMED_LITERAL_CSTRING(andStr, " AND "); + NS_NAMED_LITERAL_CSTRING(spacecolon, " :"); + NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); - nsresult - BindToStatement(mozIStorageStatement* aStatement) const; + if (IsOnly()) { + // Both keys are set and they're equal. + _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + + spacecolon + lowerKey; + } + else { + nsAutoCString clause; - void - DropJSObjects(); + if (!Lower().IsUnset()) { + // Lower key is set. + clause.Append(andStr + aKeyColumnName); + clause.AppendLiteral(" >"); + if (!IsLowerOpen()) { + clause.Append('='); + } + clause.Append(spacecolon + lowerKey); + } + + if (!Upper().IsUnset()) { + // Upper key is set. + clause.Append(andStr + aKeyColumnName); + clause.AppendLiteral(" <"); + if (!IsUpperOpen()) { + clause.Append('='); + } + clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key")); + } + + _retval = clause; + } + } + + nsresult BindToStatement(mozIStorageStatement* aStatement) const + { + NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); + + if (IsOnly()) { + return Lower().BindToStatement(aStatement, lowerKey); + } + + nsresult rv; + + if (!Lower().IsUnset()) { + rv = Lower().BindToStatement(aStatement, lowerKey); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + if (!Upper().IsUnset()) { + rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key")); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + return NS_OK; + } + + template + void ToSerializedKeyRange(T& aKeyRange); + + void DropJSObjects(); // WebIDL JSObject* @@ -169,17 +176,48 @@ public: return mUpperOpen; } + static already_AddRefed + Only(const GlobalObject& aGlobal, + JS::Handle aValue, ErrorResult& aRv); + + static already_AddRefed + LowerBound(const GlobalObject& aGlobal, + JS::Handle aValue, bool aOpen, ErrorResult& aRv); + + static already_AddRefed + UpperBound(const GlobalObject& aGlobal, + JS::Handle aValue, bool aOpen, ErrorResult& aRv); + + static already_AddRefed + Bound(const GlobalObject& aGlobal, + JS::Handle aLower, JS::Handle aUpper, + bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv); + private: IDBKeyRange(nsISupports* aGlobal, bool aLowerOpen, bool aUpperOpen, - bool aIsOnly); + bool aIsOnly) + : mGlobal(aGlobal), mCachedLowerVal(JSVAL_VOID), mCachedUpperVal(JSVAL_VOID), + mLowerOpen(aLowerOpen), mUpperOpen(aUpperOpen), mIsOnly(aIsOnly), + mHaveCachedLowerVal(false), mHaveCachedUpperVal(false), mRooted(false) + { } ~IDBKeyRange(); + + nsCOMPtr mGlobal; + Key mLower; + Key mUpper; + JS::Heap mCachedLowerVal; + JS::Heap mCachedUpperVal; + const bool mLowerOpen; + const bool mUpperOpen; + const bool mIsOnly; + bool mHaveCachedLowerVal; + bool mHaveCachedUpperVal; + bool mRooted; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbkeyrange_h__ diff --git a/dom/indexedDB/IDBMutableFile.cpp b/dom/indexedDB/IDBMutableFile.cpp index 038554f2dc67..7b4f3f040779 100644 --- a/dom/indexedDB/IDBMutableFile.cpp +++ b/dom/indexedDB/IDBMutableFile.cpp @@ -6,36 +6,26 @@ #include "IDBMutableFile.h" -#include "FileSnapshot.h" -#include "FileInfo.h" -#include "IDBDatabase.h" -#include "IDBFactory.h" -#include "IDBFileHandle.h" -#include "IDBFileRequest.h" -#include "IndexedDatabaseManager.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" +#include "nsIDOMFile.h" + #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileService.h" #include "mozilla/dom/IDBMutableFileBinding.h" #include "mozilla/dom/MetadataHelper.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "mozilla/dom/quota/FileStreams.h" #include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/ipc/BackgroundUtils.h" -#include "mozilla/ipc/PBackgroundSharedTypes.h" #include "nsContentUtils.h" #include "nsDebug.h" #include "nsError.h" -#include "nsIDOMFile.h" -#include "nsIPrincipal.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "FileSnapshot.h" +#include "IDBDatabase.h" +#include "IDBFileHandle.h" +#include "IDBFileRequest.h" -using namespace mozilla::dom::quota; -using namespace mozilla::ipc; +using namespace mozilla::dom; +USING_INDEXEDDB_NAMESPACE +USING_QUOTA_NAMESPACE namespace { @@ -58,6 +48,7 @@ public: ReleaseObjects() MOZ_OVERRIDE { mMutableFile = nullptr; + MetadataHelper::ReleaseObjects(); } @@ -65,213 +56,109 @@ private: nsRefPtr mMutableFile; }; +inline already_AddRefed GetFileFor(FileInfo* aFileInfo) + { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aFileInfo); - FileManager* fileManager = aFileInfo->Manager(); - MOZ_ASSERT(fileManager); - nsCOMPtr directory = fileManager->GetDirectory(); - if (NS_WARN_IF(!directory)) { - return nullptr; - } + NS_ENSURE_TRUE(directory, nullptr); - nsCOMPtr file = - fileManager->GetFileForId(directory, aFileInfo->Id()); - if (NS_WARN_IF(!file)) { - return nullptr; - } + nsCOMPtr file = fileManager->GetFileForId(directory, + aFileInfo->Id()); + NS_ENSURE_TRUE(file, nullptr); return file.forget(); } } // anonymous namespace -IDBMutableFile::IDBMutableFile(IDBDatabase* aDatabase, - const nsAString& aName, - const nsAString& aType, - already_AddRefed aFileInfo, - const nsACString& aGroup, - const nsACString& aOrigin, - const nsACString& aStorageId, - PersistenceType aPersistenceType, - already_AddRefed aFile) - : DOMEventTargetHelper(aDatabase) - , mDatabase(aDatabase) - , mFileInfo(aFileInfo) - , mGroup(aGroup) - , mOrigin(aOrigin) - , mPersistenceType(aPersistenceType) - , mInvalidated(false) +namespace mozilla { +namespace dom { +namespace indexedDB { + +IDBMutableFile::IDBMutableFile(IDBDatabase* aOwner) + : DOMEventTargetHelper(aOwner) { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDatabase); - MOZ_ASSERT(mFileInfo); - - mName = aName; - mType = aType; - mFile = aFile; - mStorageId = aStorageId; - mFileName.AppendInt(mFileInfo->Id()); - - MOZ_ASSERT(mFile); - - mDatabase->NoteLiveMutableFile(this); } IDBMutableFile::~IDBMutableFile() { - // XXX This is always in the main process but it sometimes happens too late in - // shutdown and the IndexedDatabaseManager has already been torn down. - // MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - if (mDatabase) { - mDatabase->NoteFinishedMutableFile(this); - } } -// static -already_AddRefed -IDBMutableFile::Create(IDBDatabase* aDatabase, - const nsAString& aName, - const nsAString& aType, - already_AddRefed aFileInfo) -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - nsRefPtr fileInfo(aFileInfo); - MOZ_ASSERT(fileInfo); - - PrincipalInfo* principalInfo = aDatabase->Factory()->GetPrincipalInfo(); - MOZ_ASSERT(principalInfo); - - nsCOMPtr principal = PrincipalInfoToPrincipal(*principalInfo); - if (NS_WARN_IF(!principal)) { - return nullptr; - } - - nsCString group; - nsCString origin; - if (NS_WARN_IF(NS_FAILED(QuotaManager::GetInfoFromPrincipal(principal, - &group, - &origin, - nullptr, - nullptr)))) { - return nullptr; - } - - const DatabaseSpec* spec = aDatabase->Spec(); - MOZ_ASSERT(spec); - - PersistenceType persistenceType = spec->metadata().persistenceType(); - - nsCString storageId; - QuotaManager::GetStorageId(persistenceType, - origin, - Client::IDB, - aDatabase->Name(), - storageId); - - nsCOMPtr file = GetFileFor(fileInfo); - if (NS_WARN_IF(!file)) { - return nullptr; - } - - nsRefPtr newFile = - new IDBMutableFile(aDatabase, - aName, - aType, - fileInfo.forget(), - group, - origin, - storageId, - persistenceType, - file.forget()); - - return newFile.forget(); -} - -void -IDBMutableFile::Invalidate() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mInvalidated); - - mInvalidated = true; -} - -NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(IDBMutableFile, DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBMutableFile, DOMEventTargetHelper, + mDatabase) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBMutableFile) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBMutableFile) +NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(IDBMutableFile, DOMEventTargetHelper) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBMutableFile, - DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +// static +already_AddRefed +IDBMutableFile::Create(const nsAString& aName, + const nsAString& aType, + IDBDatabase* aDatabase, + already_AddRefed aFileInfo) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBMutableFile, - DOMEventTargetHelper) - MOZ_ASSERT(tmp->mDatabase); - tmp->mDatabase->NoteFinishedMutableFile(tmp); + nsRefPtr fileInfo(aFileInfo); + NS_ASSERTION(fileInfo, "Null pointer!"); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDatabase) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END + nsRefPtr newFile = new IDBMutableFile(aDatabase); + + newFile->mName = aName; + newFile->mType = aType; + + newFile->mFile = GetFileFor(fileInfo); + NS_ENSURE_TRUE(newFile->mFile, nullptr); + + newFile->mStorageId = aDatabase->Id(); + newFile->mFileName.AppendInt(fileInfo->Id()); + + newFile->mDatabase = aDatabase; + fileInfo.swap(newFile->mFileInfo); + + return newFile.forget(); +} bool IDBMutableFile::IsInvalid() { - return mInvalidated; + return mDatabase->IsInvalidated(); } nsIOfflineStorage* IDBMutableFile::Storage() { - MOZ_CRASH("Don't call me!"); + return mDatabase; } already_AddRefed IDBMutableFile::CreateStream(bool aReadOnly) { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + PersistenceType persistenceType = mDatabase->Type(); + const nsACString& group = mDatabase->Group(); + const nsACString& origin = mDatabase->Origin(); nsCOMPtr result; if (aReadOnly) { nsRefPtr stream = - FileInputStream::Create(mPersistenceType, - mGroup, - mOrigin, - mFile, - -1, - -1, + FileInputStream::Create(persistenceType, group, origin, mFile, -1, -1, nsIFileInputStream::DEFER_OPEN); result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream); - } else { + } + else { nsRefPtr stream = - FileStream::Create(mPersistenceType, - mGroup, - mOrigin, - mFile, - -1, - -1, + FileStream::Create(persistenceType, group, origin, mFile, -1, -1, nsIFileStream::DEFER_OPEN); result = NS_ISUPPORTS_CAST(nsIFileStream*, stream); } - - if (NS_WARN_IF(!result)) { - return nullptr; - } + NS_ENSURE_TRUE(result, nullptr); return result.forget(); } @@ -279,37 +166,33 @@ IDBMutableFile::CreateStream(bool aReadOnly) void IDBMutableFile::SetThreadLocals() { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); MOZ_ASSERT(mDatabase->GetOwner(), "Should have owner!"); - QuotaManager::SetCurrentWindow(mDatabase->GetOwner()); } void IDBMutableFile::UnsetThreadLocals() { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - QuotaManager::SetCurrentWindow(nullptr); } +already_AddRefed +IDBMutableFile::CreateFileObject(IDBFileHandle* aFileHandle, uint32_t aFileSize) +{ + nsCOMPtr fileSnapshot = new DOMFile( + new FileImplSnapshot(mName, mType, aFileSize, mFile, aFileHandle, + mFileInfo)); + + return fileSnapshot.forget(); +} + +// virtual JSObject* IDBMutableFile::WrapObject(JSContext* aCx) { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - return IDBMutableFileBinding::Wrap(aCx, this); } -IDBDatabase* -IDBMutableFile::Database() const -{ - MOZ_ASSERT(NS_IsMainThread()); - - return mDatabase; -} - already_AddRefed IDBMutableFile::Open(FileMode aMode, ErrorResult& aError) { @@ -330,31 +213,6 @@ IDBMutableFile::Open(FileMode aMode, ErrorResult& aError) return fileHandle.forget(); } -int64_t -IDBMutableFile::GetFileId() const -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mFileInfo); - - return mFileInfo->Id(); -} - -already_AddRefed -IDBMutableFile::CreateFileObject(IDBFileHandle* aFileHandle, - MetadataParameters* aMetadataParams) -{ - nsRefPtr impl = - new FileImplSnapshot(mName, - mType, - aMetadataParams, - mFile, - aFileHandle, - mFileInfo); - - nsCOMPtr fileSnapshot = new DOMFile(impl); - return fileSnapshot.forget(); -} - already_AddRefed IDBMutableFile::GetFile(ErrorResult& aError) { @@ -369,11 +227,10 @@ IDBMutableFile::GetFile(ErrorResult& aError) IDBFileHandle::Create(FileMode::Readonly, FileHandleBase::PARALLEL, this); nsRefPtr request = - IDBFileRequest::Create(GetOwner(), - fileHandle, - /* aWrapAsDOMRequest */ true); + IDBFileRequest::Create(GetOwner(), fileHandle, /* aWrapAsDOMRequest */ + true); - nsRefPtr params = new MetadataParameters(true, true); + nsRefPtr params = new MetadataParameters(true, false); nsRefPtr helper = new GetFileHelper(fileHandle, request, params, this); @@ -387,6 +244,10 @@ IDBMutableFile::GetFile(ErrorResult& aError) return request.forget(); } +} // namespace indexedDB +} // namespace dom +} // namespace mozilla + nsresult GetFileHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) @@ -396,7 +257,7 @@ GetFileHelper::GetSuccessResult(JSContext* aCx, auto fileHandle = static_cast(mFileHandle.get()); nsCOMPtr domFile = - mMutableFile->CreateFileObject(fileHandle, mParams); + mMutableFile->CreateFileObject(fileHandle, mParams->Size()); nsresult rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), aVal); @@ -406,7 +267,3 @@ GetFileHelper::GetSuccessResult(JSContext* aCx, return NS_OK; } - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/IDBMutableFile.h b/dom/indexedDB/IDBMutableFile.h index 53c3c00be64e..bf28971cabc5 100644 --- a/dom/indexedDB/IDBMutableFile.h +++ b/dom/indexedDB/IDBMutableFile.h @@ -8,13 +8,13 @@ #define mozilla_dom_indexeddb_idbmutablefile_h__ #include "js/TypeDecls.h" -#include "mozilla/Atomics.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/dom/FileModeBinding.h" -#include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/dom/FileModeBinding.h" +#include "mozilla/dom/indexedDB/FileInfo.h" #include "mozilla/dom/MutableFile.h" -#include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/DOMEventTargetHelper.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" @@ -28,39 +28,23 @@ class ErrorResult; namespace dom { class DOMRequest; -class MetadataParameters; namespace indexedDB { -class FileInfo; class IDBDatabase; class IDBFileHandle; -class IDBMutableFile MOZ_FINAL - : public DOMEventTargetHelper - , public MutableFileBase +class IDBMutableFile MOZ_FINAL : public DOMEventTargetHelper, + public MutableFileBase { - typedef mozilla::dom::MetadataParameters MetadataParameters; - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - nsString mName; - nsString mType; - - nsRefPtr mDatabase; - nsRefPtr mFileInfo; - - const nsCString mGroup; - const nsCString mOrigin; - const PersistenceType mPersistenceType; - - Atomic mInvalidated; - public: + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper) + static already_AddRefed - Create(IDBDatabase* aDatabase, - const nsAString& aName, - const nsAString& aType, - already_AddRefed aFileInfo); + Create(const nsAString& aName, const nsAString& aType, + IDBDatabase* aDatabase, already_AddRefed aFileInfo); const nsAString& Name() const @@ -75,7 +59,10 @@ public: } int64_t - GetFileId() const; + GetFileId() const + { + return mFileInfo->Id(); + } FileInfo* GetFileInfo() const @@ -83,16 +70,6 @@ public: return mFileInfo; } - already_AddRefed - CreateFileObject(IDBFileHandle* aFileHandle, - MetadataParameters* aMetadataParams); - - void - Invalidate(); - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper) - virtual bool IsInvalid() MOZ_OVERRIDE; @@ -108,6 +85,9 @@ public: virtual void UnsetThreadLocals() MOZ_OVERRIDE; + already_AddRefed + CreateFileObject(IDBFileHandle* aFileHandle, uint32_t aFileSize); + // nsWrapperCache virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; @@ -132,7 +112,12 @@ public: } IDBDatabase* - Database() const; + Database() + { + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + + return mDatabase; + } already_AddRefed Open(FileMode aMode, ErrorResult& aError); @@ -144,17 +129,14 @@ public: IMPL_EVENT_HANDLER(error) private: - IDBMutableFile(IDBDatabase* aDatabase, - const nsAString& aName, - const nsAString& aType, - already_AddRefed aFileInfo, - const nsACString& aGroup, - const nsACString& aOrigin, - const nsACString& aStorageId, - PersistenceType aPersistenceType, - already_AddRefed aFile); - + explicit IDBMutableFile(IDBDatabase* aOwner); ~IDBMutableFile(); + + nsString mName; + nsString mType; + + nsRefPtr mDatabase; + nsRefPtr mFileInfo; }; } // namespace indexedDB diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 9170b071c68d..554d64c40de4 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -4,441 +4,756 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "base/basictypes.h" + #include "IDBObjectStore.h" -#include "FileInfo.h" +#include "mozilla/dom/ipc/nsIRemoteBlob.h" +#include "nsIOutputStream.h" + +#include +#include "jsfriendapi.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/IDBMutableFileBinding.h" +#include "mozilla/dom/nsIContentParent.h" +#include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/ipc/Blob.h" +#include "mozilla/dom/quota/FileStreams.h" +#include "mozilla/Endian.h" +#include "mozilla/storage.h" +#include "nsContentUtils.h" +#include "nsDOMClassInfo.h" +#include "nsDOMFile.h" +#include "mozilla/dom/DOMStringList.h" +#include "nsJSUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "snappy/snappy.h" + +#include "AsyncConnectionHelper.h" #include "IDBCursor.h" -#include "IDBDatabase.h" #include "IDBEvents.h" -#include "IDBFactory.h" #include "IDBIndex.h" #include "IDBKeyRange.h" #include "IDBMutableFile.h" -#include "IDBRequest.h" #include "IDBTransaction.h" -#include "IndexedDatabase.h" -#include "IndexedDatabaseInlines.h" -#include "IndexedDatabaseManager.h" -#include "js/Class.h" -#include "js/StructuredClone.h" +#include "DatabaseInfo.h" #include "KeyPath.h" -#include "mozilla/Endian.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/Move.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/DOMStringList.h" -#include "mozilla/dom/IDBMutableFileBinding.h" -#include "mozilla/dom/IDBObjectStoreBinding.h" -#include "mozilla/dom/StructuredCloneTags.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" -#include "mozilla/dom/ipc/BlobChild.h" -#include "mozilla/dom/ipc/BlobParent.h" -#include "mozilla/dom/ipc/nsIRemoteBlob.h" -#include "mozilla/ipc/BackgroundChild.h" -#include "mozilla/ipc/PBackgroundSharedTypes.h" -#include "nsCOMPtr.h" -#include "nsDOMFile.h" -#include "nsIDOMFile.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -// Include this last to avoid path problems on Windows. -#include "ActorsChild.h" +#include "ipc/IndexedDBChild.h" +#include "ipc/IndexedDBParent.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#include "IndexedDatabaseInlines.h" +#include "nsCharSeparatedTokenizer.h" -using namespace mozilla::dom::quota; -using namespace mozilla::ipc; +#define FILE_COPY_BUFFER_SIZE 32768 -struct IDBObjectStore::StructuredCloneWriteInfo -{ - struct BlobOrFileInfo - { - nsCOMPtr mBlob; - nsRefPtr mFileInfo; +USING_INDEXEDDB_NAMESPACE +using namespace mozilla::dom; +using namespace mozilla::dom::indexedDB::ipc; +using mozilla::dom::quota::FileOutputStream; +using mozilla::ErrorResult; +using mozilla::fallible_t; +using mozilla::LittleEndian; +using mozilla::Move; +using mozilla::NativeEndian; - bool - operator==(const BlobOrFileInfo& aOther) const - { - return this->mBlob == aOther.mBlob && this->mFileInfo == aOther.mFileInfo; - } - }; +BEGIN_INDEXEDDB_NAMESPACE - JSAutoStructuredCloneBuffer mCloneBuffer; - nsTArray mBlobOrFileInfos; - IDBDatabase* mDatabase; - uint64_t mOffsetToKeyProp; - - explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase) - : mDatabase(aDatabase) - , mOffsetToKeyProp(0) - { - MOZ_ASSERT(aDatabase); - - MOZ_COUNT_CTOR(StructuredCloneWriteInfo); - } - - StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo) - : mCloneBuffer(Move(aCloneWriteInfo.mCloneBuffer)) - , mDatabase(aCloneWriteInfo.mDatabase) - , mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp) - { - MOZ_ASSERT(mDatabase); - - MOZ_COUNT_CTOR(StructuredCloneWriteInfo); - - mBlobOrFileInfos.SwapElements(aCloneWriteInfo.mBlobOrFileInfos); - aCloneWriteInfo.mOffsetToKeyProp = 0; - } - - ~StructuredCloneWriteInfo() - { - MOZ_COUNT_DTOR(StructuredCloneWriteInfo); - } - - bool - operator==(const StructuredCloneWriteInfo& aOther) const - { - return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() && - this->mCloneBuffer.data() == aOther.mCloneBuffer.data() && - this->mBlobOrFileInfos == aOther.mBlobOrFileInfos && - this->mDatabase == aOther.mDatabase && - this->mOffsetToKeyProp == aOther.mOffsetToKeyProp; - } - - bool - SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther) - { - if (aOther.data().IsEmpty()) { - mCloneBuffer.clear(); - } else { - auto* aOtherBuffer = - reinterpret_cast( - const_cast(aOther.data().Elements())); - if (!mCloneBuffer.copy(aOtherBuffer, aOther.data().Length())) { - return false; - } - } - - mBlobOrFileInfos.Clear(); - - mOffsetToKeyProp = aOther.offsetToKeyProp(); - return true; - } -}; - -namespace { - -struct MOZ_STACK_CLASS MutableFileData MOZ_FINAL +struct MutableFileData { nsString type; nsString name; - - MutableFileData() - { - MOZ_COUNT_CTOR(MutableFileData); - } - - ~MutableFileData() - { - MOZ_COUNT_DTOR(MutableFileData); - } }; -struct MOZ_STACK_CLASS BlobOrFileData MOZ_FINAL +struct BlobOrFileData { + BlobOrFileData() + : tag(0), size(0), lastModifiedDate(UINT64_MAX) + { } + uint32_t tag; uint64_t size; nsString type; nsString name; uint64_t lastModifiedDate; - - BlobOrFileData() - : tag(0) - , size(0) - , lastModifiedDate(UINT64_MAX) - { - MOZ_COUNT_CTOR(BlobOrFileData); - } - - ~BlobOrFileData() - { - MOZ_COUNT_DTOR(BlobOrFileData); - } }; -struct MOZ_STACK_CLASS GetAddInfoClosure MOZ_FINAL +END_INDEXEDDB_NAMESPACE + +namespace { + +inline +bool +IgnoreNothing(char16_t c) { - IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo; - JS::Handle mValue; + return false; +} - GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo, - JS::Handle aValue) - : mCloneWriteInfo(aCloneWriteInfo) - , mValue(aValue) +class ObjectStoreHelper : public AsyncConnectionHelper +{ +public: + ObjectStoreHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore) + : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore), + mActor(nullptr) { - MOZ_COUNT_CTOR(GetAddInfoClosure); + NS_ASSERTION(aTransaction, "Null transaction!"); + NS_ASSERTION(aRequest, "Null request!"); + NS_ASSERTION(aObjectStore, "Null object store!"); } - ~GetAddInfoClosure() + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) = 0; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; + +protected: + nsRefPtr mObjectStore; + +private: + IndexedDBObjectStoreRequestChild* mActor; +}; + +class NoRequestObjectStoreHelper : public AsyncConnectionHelper +{ +public: + NoRequestObjectStoreHelper(IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore) + : AsyncConnectionHelper(aTransaction, nullptr), mObjectStore(aObjectStore) { - MOZ_COUNT_DTOR(GetAddInfoClosure); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aTransaction, "Null transaction!"); + NS_ASSERTION(aObjectStore, "Null object store!"); + } + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult OnSuccess() MOZ_OVERRIDE; + + virtual void OnError() MOZ_OVERRIDE; + +protected: + nsRefPtr mObjectStore; +}; + +class AddHelper : public ObjectStoreHelper +{ +public: + AddHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + StructuredCloneWriteInfo&& aCloneWriteInfo, + const Key& aKey, + bool aOverwrite, + nsTArray& aIndexUpdateInfo) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mCloneWriteInfo(Move(aCloneWriteInfo)), + mKey(aKey), + mOverwrite(aOverwrite) + { + mIndexUpdateInfo.SwapElements(aIndexUpdateInfo); + } + + ~AddHelper() + { + IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +private: + // These may change in the autoincrement case. + StructuredCloneWriteInfo mCloneWriteInfo; + Key mKey; + nsTArray mIndexUpdateInfo; + const bool mOverwrite; +}; + +class GetHelper : public ObjectStoreHelper +{ +public: + GetHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mKeyRange(aKeyRange) + { + NS_ASSERTION(aKeyRange, "Null key range!"); + } + + ~GetHelper() + { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + // In-params. + nsRefPtr mKeyRange; + +private: + // Out-params. + StructuredCloneReadInfo mCloneReadInfo; +}; + +class DeleteHelper : public GetHelper +{ +public: + DeleteHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange) + : GetHelper(aTransaction, aRequest, aObjectStore, aKeyRange) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; +}; + +class ClearHelper : public ObjectStoreHelper +{ +public: + ClearHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; +}; + +class OpenCursorHelper : public ObjectStoreHelper +{ +public: + OpenCursorHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange, + IDBCursor::Direction aDirection) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mKeyRange(aKeyRange), mDirection(aDirection) + { } + + ~OpenCursorHelper() + { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +private: + nsresult EnsureCursor(); + + // In-params. + nsRefPtr mKeyRange; + const IDBCursor::Direction mDirection; + + // Out-params. + Key mKey; + StructuredCloneReadInfo mCloneReadInfo; + nsCString mContinueQuery; + nsCString mContinueToQuery; + Key mRangeKey; + + // Only used in the parent process. + nsRefPtr mCursor; + SerializedStructuredCloneReadInfo mSerializedCloneReadInfo; +}; + +class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper +{ +public: + OpenKeyCursorHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange, + IDBCursor::Direction aDirection) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mKeyRange(aKeyRange), mDirection(aDirection) + { } + + virtual nsresult + DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; + + virtual nsresult + GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) + MOZ_OVERRIDE; + + virtual void + ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +private: + ~OpenKeyCursorHelper() + { } + + nsresult EnsureCursor(); + + // In-params. + nsRefPtr mKeyRange; + const IDBCursor::Direction mDirection; + + // Out-params. + Key mKey; + nsCString mContinueQuery; + nsCString mContinueToQuery; + Key mRangeKey; + + // Only used in the parent process. + nsRefPtr mCursor; +}; + +class CreateIndexHelper : public NoRequestObjectStoreHelper +{ +public: + CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex) + : NoRequestObjectStoreHelper(aTransaction, aIndex->ObjectStore()), + mIndex(aIndex) + { + if (sTLSIndex == BAD_TLS_INDEX) { + PR_NewThreadPrivateIndex(&sTLSIndex, DestroyTLSEntry); + } + + NS_ASSERTION(sTLSIndex != BAD_TLS_INDEX, + "PR_NewThreadPrivateIndex failed!"); + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + +private: + nsresult InsertDataFromObjectStore(mozIStorageConnection* aConnection); + + static void DestroyTLSEntry(void* aPtr); + + static unsigned sTLSIndex; + + // In-params. + nsRefPtr mIndex; +}; + +unsigned CreateIndexHelper::sTLSIndex = unsigned(BAD_TLS_INDEX); + +class DeleteIndexHelper : public NoRequestObjectStoreHelper +{ +public: + DeleteIndexHelper(IDBTransaction* aTransaction, + IDBObjectStore* aObjectStore, + const nsAString& aName) + : NoRequestObjectStoreHelper(aTransaction, aObjectStore), mName(aName) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + +private: + // In-params + nsString mName; +}; + +class GetAllHelper : public ObjectStoreHelper +{ +public: + GetAllHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange, + const uint32_t aLimit) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mKeyRange(aKeyRange), mLimit(aLimit) + { } + + ~GetAllHelper() + { + for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); + } + } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +protected: + // In-params. + nsRefPtr mKeyRange; + const uint32_t mLimit; + +private: + // Out-params. + nsTArray mCloneReadInfos; +}; + +class GetAllKeysHelper MOZ_FINAL : public ObjectStoreHelper +{ +public: + GetAllKeysHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange, + const uint32_t aLimit) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mKeyRange(aKeyRange), mLimit(aLimit) + { } + + virtual nsresult + DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; + + virtual nsresult + GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) + MOZ_OVERRIDE; + + virtual void + ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +private: + ~GetAllKeysHelper() + { } + + nsRefPtr mKeyRange; + const uint32_t mLimit; + nsTArray mKeys; +}; + +class CountHelper : public ObjectStoreHelper +{ +public: + CountHelper(IDBTransaction* aTransaction, + IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + IDBKeyRange* aKeyRange) + : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), + mKeyRange(aKeyRange), mCount(0) + { } + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + virtual nsresult + PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + +private: + nsRefPtr mKeyRange; + uint64_t mCount; +}; + +class MOZ_STACK_CLASS AutoRemoveIndex +{ +public: + AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo, + const nsAString& aIndexName) + : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName) + { } + + ~AutoRemoveIndex() + { + if (mObjectStoreInfo) { + for (uint32_t i = 0; i < mObjectStoreInfo->indexes.Length(); i++) { + if (mObjectStoreInfo->indexes[i].name == mIndexName) { + mObjectStoreInfo->indexes.RemoveElementAt(i); + break; + } + } + } + } + + void forget() + { + mObjectStoreInfo = nullptr; + } + +private: + ObjectStoreInfo* mObjectStoreInfo; + nsString mIndexName; +}; + +class ThreadLocalJSRuntime +{ + JSRuntime* mRuntime; + JSContext* mContext; + JSObject* mGlobal; + + static const JSClass sGlobalClass; + static const unsigned sRuntimeHeapSize = 768 * 1024; + + ThreadLocalJSRuntime() + : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr) + { + MOZ_COUNT_CTOR(ThreadLocalJSRuntime); + } + + nsresult Init() + { + mRuntime = JS_NewRuntime(sRuntimeHeapSize); + NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY); + + /* + * Not setting this will cause JS_CHECK_RECURSION to report false + * positives + */ + JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024); + + mContext = JS_NewContext(mRuntime, 0); + NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY); + + JSAutoRequest ar(mContext); + + mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr, + JS::FireOnNewGlobalHook); + NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY); + + return NS_OK; + } + + public: + static ThreadLocalJSRuntime *Create() + { + ThreadLocalJSRuntime *entry = new ThreadLocalJSRuntime(); + NS_ENSURE_TRUE(entry, nullptr); + + if (NS_FAILED(entry->Init())) { + delete entry; + return nullptr; + } + + return entry; + } + + JSContext *Context() const + { + return mContext; + } + + JSObject *Global() const + { + return mGlobal; + } + + ~ThreadLocalJSRuntime() + { + MOZ_COUNT_DTOR(ThreadLocalJSRuntime); + + if (mContext) { + JS_DestroyContext(mContext); + } + + if (mRuntime) { + JS_DestroyRuntime(mRuntime); + } } }; +const JSClass ThreadLocalJSRuntime::sGlobalClass = { + "IndexedDBTransactionThreadGlobal", + JSCLASS_GLOBAL_FLAGS, + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, + nullptr, nullptr, nullptr, nullptr, + JS_GlobalObjectTraceHook +}; + +inline already_AddRefed GenerateRequest(IDBObjectStore* aObjectStore) { - MOZ_ASSERT(aObjectStore); - aObjectStore->AssertIsOnOwningThread(); - - IDBTransaction* transaction = aObjectStore->Transaction(); - - nsRefPtr request = - IDBRequest::Create(aObjectStore, transaction->Database(), transaction); - MOZ_ASSERT(request); - - return request.forget(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + IDBDatabase* database = aObjectStore->Transaction()->Database(); + return IDBRequest::Create(aObjectStore, database, + aObjectStore->Transaction()); } -bool -StructuredCloneWriteCallback(JSContext* aCx, - JSStructuredCloneWriter* aWriter, - JS::Handle aObj, - void* aClosure) +struct MOZ_STACK_CLASS GetAddInfoClosure { - MOZ_ASSERT(aCx); - MOZ_ASSERT(aWriter); - MOZ_ASSERT(aClosure); - - auto* cloneWriteInfo = - static_cast(aClosure); - - if (JS_GetClass(aObj) == IDBObjectStore::DummyPropClass()) { - MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp); - cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter); - - uint64_t value = 0; - // Omit endian swap - return JS_WriteBytes(aWriter, &value, sizeof(value)); - } - - IDBMutableFile* mutableFile; - if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) { - IDBDatabase* database = mutableFile->Database(); - MOZ_ASSERT(database); - - // Throw when trying to store IDBMutableFile objects that live in a - // different database. - if (database != cloneWriteInfo->mDatabase) { - MOZ_ASSERT(!SameCOMIdentity(database, cloneWriteInfo->mDatabase)); - - if (database->Name() != cloneWriteInfo->mDatabase->Name()) { - return false; - } - - nsCString fileOrigin, databaseOrigin; - PersistenceType filePersistenceType, databasePersistenceType; - - if (NS_WARN_IF(NS_FAILED(database->GetQuotaInfo(fileOrigin, - &filePersistenceType)))) { - return false; - } - - if (NS_WARN_IF(NS_FAILED(cloneWriteInfo->mDatabase->GetQuotaInfo( - databaseOrigin, - &databasePersistenceType)))) { - return false; - } - - if (filePersistenceType != databasePersistenceType || - fileOrigin != databaseOrigin) { - return false; - } - } - - nsRefPtr fileInfo = mutableFile->GetFileInfo(); - MOZ_ASSERT(fileInfo); - - if (cloneWriteInfo->mBlobOrFileInfos.Length() > size_t(UINT32_MAX)) { - MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!"); - return false; - } - - const uint32_t index = uint32_t(cloneWriteInfo->mBlobOrFileInfos.Length()); - - NS_ConvertUTF16toUTF8 convType(mutableFile->Type()); - uint32_t convTypeLength = - NativeEndian::swapToLittleEndian(convType.Length()); - - NS_ConvertUTF16toUTF8 convName(mutableFile->Name()); - uint32_t convNameLength = - NativeEndian::swapToLittleEndian(convName.Length()); - - if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, uint32_t(index)) || - !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) || - !JS_WriteBytes(aWriter, convType.get(), convType.Length()) || - !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) || - !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { - return false; - } - - IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo* - newBlobOrFileInfo = - cloneWriteInfo->mBlobOrFileInfos.AppendElement(); - newBlobOrFileInfo->mFileInfo.swap(fileInfo); - - return true; - } - - MOZ_ASSERT(NS_IsMainThread(), "This can't work off the main thread!"); - - nsCOMPtr wrappedNative; - nsContentUtils::XPConnect()-> - GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); - - if (wrappedNative) { - nsISupports* supports = wrappedNative->Native(); - - nsCOMPtr blob = do_QueryInterface(supports); - if (blob) { - uint64_t size; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blob->GetSize(&size))); - - size = NativeEndian::swapToLittleEndian(size); - - nsString type; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blob->GetType(type))); - - NS_ConvertUTF16toUTF8 convType(type); - uint32_t convTypeLength = - NativeEndian::swapToLittleEndian(convType.Length()); - - nsCOMPtr file = do_QueryInterface(blob); - - if (cloneWriteInfo->mBlobOrFileInfos.Length() > size_t(UINT32_MAX)) { - MOZ_ASSERT(false, - "Fix the structured clone data to use a bigger type!"); - return false; - } - - const uint32_t index = - uint32_t(cloneWriteInfo->mBlobOrFileInfos.Length()); - - if (!JS_WriteUint32Pair(aWriter, - file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, - index) || - !JS_WriteBytes(aWriter, &size, sizeof(size)) || - !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) || - !JS_WriteBytes(aWriter, convType.get(), convType.Length())) { - return false; - } - - if (file) { - uint64_t lastModifiedDate; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - file->GetMozLastModifiedDate(&lastModifiedDate))); - - lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate); - - nsString name; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(file->GetName(name))); - - NS_ConvertUTF16toUTF8 convName(name); - uint32_t convNameLength = - NativeEndian::swapToLittleEndian(convName.Length()); - - if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) || - !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) || - !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { - return false; - } - } - - IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo* - newBlobOrFileInfo = - cloneWriteInfo->mBlobOrFileInfos.AppendElement(); - newBlobOrFileInfo->mBlob.swap(blob); - - return true; - } - } - - // Try using the runtime callbacks - const JSStructuredCloneCallbacks* runtimeCallbacks = - js::GetContextStructuredCloneCallbacks(aCx); - if (runtimeCallbacks) { - return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr); - } - - return false; -} + IDBObjectStore* mThis; + StructuredCloneWriteInfo& mCloneWriteInfo; + JS::Handle mValue; +}; nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) { - static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = { - nullptr /* read */, - StructuredCloneWriteCallback /* write */, - nullptr /* reportError */, - nullptr /* readTransfer */, - nullptr /* writeTransfer */, - nullptr /* freeTransfer */ - }; - - MOZ_ASSERT(aCx); - - auto* data = static_cast(aClosure); - MOZ_ASSERT(data); + GetAddInfoClosure* data = static_cast(aClosure); data->mCloneWriteInfo.mOffsetToKeyProp = 0; + data->mCloneWriteInfo.mTransaction = data->mThis->Transaction(); - if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx, - data->mValue, - &kStructuredCloneCallbacks, - &data->mCloneWriteInfo)) { + if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo, data->mValue)) { return NS_ERROR_DOM_DATA_CLONE_ERR; } return NS_OK; } +inline BlobChild* ActorFromRemoteBlob(nsIDOMBlob* aBlob) { - MOZ_ASSERT(aBlob); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsRefPtr blob = static_cast(aBlob); nsCOMPtr remoteBlob = do_QueryInterface(blob->Impl()); if (remoteBlob) { - BlobChild* actor = remoteBlob->GetBlobChild(); - MOZ_ASSERT(actor); - - if (actor->GetContentManager()) { - return nullptr; - } - - MOZ_ASSERT(actor->GetBackgroundManager()); - MOZ_ASSERT(BackgroundChild::GetForCurrentThread()); - MOZ_ASSERT(actor->GetBackgroundManager() == - BackgroundChild::GetForCurrentThread(), - "Blob actor is not bound to this thread!"); - + BlobChild* actor = + static_cast(static_cast(remoteBlob->GetPBlob())); + NS_ASSERTION(actor, "Null actor?!"); return actor; } - return nullptr; } +inline bool -ResolveMysteryFile(nsIDOMBlob* aBlob, - const nsString& aName, - const nsString& aContentType, - uint64_t aSize, +ResolveMysteryFile(nsIDOMBlob* aBlob, const nsString& aName, + const nsString& aContentType, uint64_t aSize, uint64_t aLastModifiedDate) { BlobChild* actor = ActorFromRemoteBlob(aBlob); @@ -449,9 +764,9 @@ ResolveMysteryFile(nsIDOMBlob* aBlob, return true; } +inline bool -ResolveMysteryBlob(nsIDOMBlob* aBlob, - const nsString& aContentType, +ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType, uint64_t aSize) { BlobChild* actor = ActorFromRemoteBlob(aBlob); @@ -461,7 +776,624 @@ ResolveMysteryBlob(nsIDOMBlob* aBlob, return true; } +class MainThreadDeserializationTraits +{ +public: + static bool CreateAndWrapMutableFile(JSContext* aCx, + IDBDatabase* aDatabase, + StructuredCloneFile& aFile, + const MutableFileData& aData, + JS::MutableHandle aResult) + { + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr& fileInfo = aFile.mFileInfo; + + nsRefPtr mutableFile = IDBMutableFile::Create(aData.name, + aData.type, aDatabase, fileInfo.forget()); + + JS::Rooted result(aCx, mutableFile->WrapObject(aCx)); + if (NS_WARN_IF(!result)) { + return false; + } + + aResult.set(result); + return true; + } + + static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx, + IDBDatabase* aDatabase, + StructuredCloneFile& aFile, + const BlobOrFileData& aData) + { + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || + aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || + aData.tag == SCTAG_DOM_BLOB); + + nsresult rv = NS_OK; + + nsRefPtr& fileInfo = aFile.mFileInfo; + + nsCOMPtr nativeFile; + if (!aFile.mFile) { + FileManager* fileManager = aDatabase->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + nsCOMPtr directory = fileManager->GetDirectory(); + if (!directory) { + NS_WARNING("Failed to get directory!"); + return nullptr; + } + + nativeFile = fileManager->GetFileForId(directory, fileInfo->Id()); + if (!nativeFile) { + NS_WARNING("Failed to get file!"); + return nullptr; + } + } + + if (aData.tag == SCTAG_DOM_BLOB) { + nsCOMPtr domBlob; + if (aFile.mFile) { + if (!ResolveMysteryBlob(aFile.mFile, aData.type, aData.size)) { + return nullptr; + } + domBlob = aFile.mFile; + } + else { + domBlob = DOMFile::CreateFromFile(aData.type, aData.size, nativeFile, + fileInfo); + } + + JS::Rooted wrappedBlob(aCx); + rv = nsContentUtils::WrapNative(aCx, domBlob, &NS_GET_IID(nsIDOMBlob), + &wrappedBlob); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to wrap native!"); + return nullptr; + } + + return wrappedBlob.toObjectOrNull(); + } + + nsCOMPtr domFile; + if (aFile.mFile) { + if (!ResolveMysteryFile(aFile.mFile, aData.name, aData.type, aData.size, + aData.lastModifiedDate)) { + return nullptr; + } + domFile = do_QueryInterface(aFile.mFile); + NS_ASSERTION(domFile, "This should never fail!"); + } + else { + domFile = DOMFile::CreateFromFile(aData.name, aData.type, aData.size, + nativeFile, fileInfo); + } + + JS::Rooted wrappedFile(aCx); + rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), + &wrappedFile); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to wrap native!"); + return nullptr; + } + + return wrappedFile.toObjectOrNull(); + } +}; + + +class CreateIndexDeserializationTraits +{ +public: + static bool CreateAndWrapMutableFile(JSContext* aCx, + IDBDatabase* aDatabase, + StructuredCloneFile& aFile, + const MutableFileData& aData, + JS::MutableHandle aResult) + { + // MutableFile can't be used in index creation, so just make a dummy object. + JS::Rooted obj(aCx, + JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + + if (NS_WARN_IF(!obj)) { + return false; + } + + aResult.set(obj); + return true; + } + + static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx, + IDBDatabase* aDatabase, + StructuredCloneFile& aFile, + const BlobOrFileData& aData) + { + MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || + aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || + aData.tag == SCTAG_DOM_BLOB); + + // The following properties are available for use in index creation + // Blob.size + // Blob.type + // File.name + // File.lastModifiedDate + + JS::Rooted obj(aCx, + JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + if (!obj) { + NS_WARNING("Failed to create object!"); + return nullptr; + } + + // Technically these props go on the proto, but this detail won't change + // the results of index creation. + + JS::Rooted type(aCx, + JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length())); + if (!type || + !JS_DefineProperty(aCx, obj, "size", double(aData.size), 0) || + !JS_DefineProperty(aCx, obj, "type", type, 0)) { + return nullptr; + } + + if (aData.tag == SCTAG_DOM_BLOB) { + return obj; + } + + JS::Rooted name(aCx, + JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length())); + JS::Rooted date(aCx, + JS_NewDateObjectMsec(aCx, aData.lastModifiedDate)); + if (!name || !date || + !JS_DefineProperty(aCx, obj, "name", name, 0) || + !JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0)) { + return nullptr; + } + + return obj; + } +}; + +} // anonymous namespace + +const JSClass IDBObjectStore::sDummyPropJSClass = { + "dummy", 0, + JS_PropertyStub, JS_DeletePropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub +}; + +// static +already_AddRefed +IDBObjectStore::Create(IDBTransaction* aTransaction, + ObjectStoreInfo* aStoreInfo, + const nsACString& aDatabaseId, + bool aCreating) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsRefPtr objectStore = new IDBObjectStore(); + + objectStore->mTransaction = aTransaction; + objectStore->mName = aStoreInfo->name; + objectStore->mId = aStoreInfo->id; + objectStore->mKeyPath = aStoreInfo->keyPath; + objectStore->mAutoIncrement = aStoreInfo->autoIncrement; + objectStore->mDatabaseId = aDatabaseId; + objectStore->mInfo = aStoreInfo; + + if (!IndexedDatabaseManager::IsMainProcess()) { + IndexedDBTransactionChild* transactionActor = aTransaction->GetActorChild(); + NS_ASSERTION(transactionActor, "Must have an actor here!"); + + ObjectStoreConstructorParams params; + + if (aCreating) { + CreateObjectStoreParams createParams; + createParams.info() = *aStoreInfo; + params = createParams; + } + else { + GetObjectStoreParams getParams; + getParams.name() = aStoreInfo->name; + params = getParams; + } + + IndexedDBObjectStoreChild* actor = + new IndexedDBObjectStoreChild(objectStore); + + transactionActor->SendPIndexedDBObjectStoreConstructor(actor, params); + } + + return objectStore.forget(); +} + +// static +nsresult +IDBObjectStore::AppendIndexUpdateInfo( + int64_t aIndexID, + const KeyPath& aKeyPath, + bool aUnique, + bool aMultiEntry, + JSContext* aCx, + JS::Handle aVal, + nsTArray& aUpdateInfoArray) +{ + nsresult rv; + + if (!aMultiEntry) { + Key key; + rv = aKeyPath.ExtractKey(aCx, aVal, key); + + // If an index's keypath doesn't match an object, we ignore that object. + if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { + return NS_OK; + } + + if (NS_FAILED(rv)) { + return rv; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId = aIndexID; + updateInfo->indexUnique = aUnique; + updateInfo->value = key; + + return NS_OK; + } + + JS::Rooted val(aCx); + if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) { + return NS_OK; + } + + if (JS_IsArrayObject(aCx, val)) { + JS::Rooted array(aCx, &val.toObject()); + uint32_t arrayLength; + if (!JS_GetArrayLength(aCx, array, &arrayLength)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + JS::Rooted arrayItem(aCx); + if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + Key value; + if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || + value.IsUnset()) { + // Not a value we can do anything with, ignore it. + continue; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId = aIndexID; + updateInfo->indexUnique = aUnique; + updateInfo->value = value; + } + } + else { + Key value; + if (NS_FAILED(value.SetFromJSVal(aCx, val)) || + value.IsUnset()) { + // Not a value we can do anything with, ignore it. + return NS_OK; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId = aIndexID; + updateInfo->indexUnique = aUnique; + updateInfo->value = value; + } + + return NS_OK; +} + +// static +nsresult +IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, + int64_t aObjectStoreId, + const Key& aObjectStoreKey, + bool aOverwrite, + int64_t aObjectDataId, + const nsTArray& aUpdateInfoArray) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("IDBObjectStore", "UpdateIndexes", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + NS_ASSERTION(aObjectDataId != INT64_MIN, "Bad objectData id!"); + + NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); + + if (aOverwrite) { + nsCOMPtr deleteStmt = + aTransaction->GetCachedStatement( + "DELETE FROM unique_index_data " + "WHERE object_data_id = :object_data_id; " + "DELETE FROM index_data " + "WHERE object_data_id = :object_data_id"); + NS_ENSURE_TRUE(deleteStmt, NS_ERROR_FAILURE); + + mozStorageStatementScoper scoper(deleteStmt); + + rv = deleteStmt->BindInt64ByName(objectDataId, aObjectDataId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = deleteStmt->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Avoid lots of hash lookups for objectStores with lots of indexes by lazily + // holding the necessary statements on the stack outside the loop. + nsCOMPtr insertUniqueStmt; + nsCOMPtr insertStmt; + + uint32_t infoCount = aUpdateInfoArray.Length(); + for (uint32_t i = 0; i < infoCount; i++) { + const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i]; + + nsCOMPtr& stmt = + updateInfo.indexUnique ? insertUniqueStmt : insertStmt; + + if (!stmt) { + stmt = updateInfo.indexUnique ? + aTransaction->GetCachedStatement( + "INSERT INTO unique_index_data " + "(index_id, object_data_id, object_data_key, value) " + "VALUES (:index_id, :object_data_id, :object_data_key, :value)") : + aTransaction->GetCachedStatement( + "INSERT OR IGNORE INTO index_data (" + "index_id, object_data_id, object_data_key, value) " + "VALUES (:index_id, :object_data_id, :object_data_key, :value)"); + } + NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); + + mozStorageStatementScoper scoper(stmt); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + updateInfo.indexId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aObjectStoreKey.BindToStatement(stmt, + NS_LITERAL_CSTRING("object_data_key")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) { + // If we're inserting multiple entries for the same unique index, then + // we might have failed to insert due to colliding with another entry for + // the same index in which case we should ignore it. + + for (int32_t j = (int32_t)i - 1; + j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId; + --j) { + if (updateInfo.value == aUpdateInfoArray[j].value) { + // We found a key with the same value for the same index. So we + // must have had a collision with a value we just inserted. + rv = NS_OK; + break; + } + } + } + + if (NS_FAILED(rv)) { + return rv; + } + } + + return NS_OK; +} + +// static +nsresult +IDBObjectStore::GetStructuredCloneReadInfoFromStatement( + mozIStorageStatement* aStatement, + uint32_t aDataIndex, + uint32_t aFileIdsIndex, + IDBDatabase* aDatabase, + StructuredCloneReadInfo& aInfo) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("IDBObjectStore", "GetStructuredCloneReadInfoFromStatement", + js::ProfileEntry::Category::STORAGE); + +#ifdef DEBUG + { + int32_t type; + NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) && + type == mozIStorageStatement::VALUE_TYPE_BLOB, + "Bad value type!"); + } +#endif + + const uint8_t* blobData; + uint32_t blobDataLength; + nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength, + &blobData); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + const char* compressed = reinterpret_cast(blobData); + size_t compressedLength = size_t(blobDataLength); + + static const fallible_t fallible = fallible_t(); + + size_t uncompressedLength; + if (!snappy::GetUncompressedLength(compressed, compressedLength, + &uncompressedLength)) { + IDB_WARNING("Snappy can't determine uncompressed length!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsAutoArrayPtr uncompressed(new (fallible) char[uncompressedLength]); + NS_ENSURE_TRUE(uncompressed, NS_ERROR_OUT_OF_MEMORY); + + if (!snappy::RawUncompress(compressed, compressedLength, + uncompressed.get())) { + IDB_WARNING("Snappy can't determine uncompressed length!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer; + if (!buffer.copy(reinterpret_cast(uncompressed.get()), + uncompressedLength)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + bool isNull; + rv = aStatement->GetIsNull(aFileIdsIndex, &isNull); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!isNull) { + nsString ids; + rv = aStatement->GetString(aFileIdsIndex, ids); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsAutoTArray array; + rv = ConvertFileIdsToArray(ids, array); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + FileManager* fileManager = aDatabase->Manager(); + + for (uint32_t i = 0; i < array.Length(); i++) { + const int64_t& id = array[i]; + + nsRefPtr fileInfo = fileManager->GetFileInfo(id); + NS_ASSERTION(fileInfo, "Null file info!"); + + StructuredCloneFile* file = aInfo.mFiles.AppendElement(); + file->mFileInfo.swap(fileInfo); + } + } + + aInfo.mDatabase = aDatabase; + + return NS_OK; +} + +// static +void +IDBObjectStore::ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo) +{ + // This is kind of tricky, we only want to release stuff on the main thread, + // but we can end up being called on other threads if we have already been + // cleared on the main thread. + if (!aWriteInfo.mCloneBuffer.data() && !aWriteInfo.mFiles.Length()) { + return; + } + + // If there's something to clear, we should be on the main thread. + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + ClearStructuredCloneBuffer(aWriteInfo.mCloneBuffer); + aWriteInfo.mFiles.Clear(); +} + +// static +void +IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo) +{ + // This is kind of tricky, we only want to release stuff on the main thread, + // but we can end up being called on other threads if we have already been + // cleared on the main thread. + if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) { + return; + } + + // If there's something to clear, we should be on the main thread. + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer); + aReadInfo.mFiles.Clear(); +} + +// static +void +IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer) +{ + if (aBuffer.data()) { + aBuffer.clear(); + } +} + +// static bool +IDBObjectStore::DeserializeValue(JSContext* aCx, + StructuredCloneReadInfo& aCloneReadInfo, + JS::MutableHandle aValue) +{ + NS_ASSERTION(NS_IsMainThread(), + "Should only be deserializing on the main thread!"); + NS_ASSERTION(aCx, "A JSContext is required!"); + + JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer; + + if (!buffer.data()) { + aValue.setUndefined(); + return true; + } + + JSAutoRequest ar(aCx); + + JSStructuredCloneCallbacks callbacks = { + IDBObjectStore::StructuredCloneReadCallback, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + + return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo); +} + +// static +bool +IDBObjectStore::SerializeValue(JSContext* aCx, + StructuredCloneWriteInfo& aCloneWriteInfo, + JS::Handle aValue) +{ + NS_ASSERTION(NS_IsMainThread(), + "Should only be serializing on the main thread!"); + NS_ASSERTION(aCx, "A JSContext is required!"); + + JSAutoRequest ar(aCx); + + JSStructuredCloneCallbacks callbacks = { + nullptr, + StructuredCloneWriteCallback, + nullptr, + nullptr, + nullptr, + nullptr + }; + + JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer; + + return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo); +} + +static inline bool StructuredCloneReadString(JSStructuredCloneReader* aReader, nsCString& aString) { @@ -486,11 +1418,13 @@ StructuredCloneReadString(JSStructuredCloneReader* aReader, return true; } +// static bool -ReadFileHandle(JSStructuredCloneReader* aReader, - MutableFileData* aRetval) +IDBObjectStore::ReadMutableFile(JSStructuredCloneReader* aReader, + MutableFileData* aRetval) { - static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!"); + static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, + "Update me!"); MOZ_ASSERT(aReader && aRetval); nsCString type; @@ -508,36 +1442,35 @@ ReadFileHandle(JSStructuredCloneReader* aReader, return true; } +// static bool -ReadBlobOrFile(JSStructuredCloneReader* aReader, - uint32_t aTag, - BlobOrFileData* aRetval) +IDBObjectStore::ReadBlobOrFile(JSStructuredCloneReader* aReader, + uint32_t aTag, + BlobOrFileData* aRetval) { - static_assert(SCTAG_DOM_BLOB == 0xffff8001 && - SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 && - SCTAG_DOM_FILE == 0xffff8005, + static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 && + SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 && + SCTAG_DOM_FILE == 0xFFFF8005, "Update me!"); - - MOZ_ASSERT(aReader); + MOZ_ASSERT(aReader && aRetval); MOZ_ASSERT(aTag == SCTAG_DOM_FILE || aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || aTag == SCTAG_DOM_BLOB); - MOZ_ASSERT(aRetval); aRetval->tag = aTag; + // If it's not a MutableFile, it's a Blob or a File. uint64_t size; - if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) { + if (!JS_ReadBytes(aReader, &size, sizeof(uint64_t))) { + NS_WARNING("Failed to read size!"); return false; } - aRetval->size = NativeEndian::swapFromLittleEndian(size); nsCString type; - if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) { + if (!StructuredCloneReadString(aReader, type)) { return false; } - CopyUTF8toUTF16(type, aRetval->type); // Blobs are done. @@ -545,261 +1478,73 @@ ReadBlobOrFile(JSStructuredCloneReader* aReader, return true; } - MOZ_ASSERT(aTag == SCTAG_DOM_FILE || - aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE); + NS_ASSERTION(aTag == SCTAG_DOM_FILE || + aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE, "Huh?!"); uint64_t lastModifiedDate; if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) { lastModifiedDate = UINT64_MAX; - } else { - if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate, - sizeof(lastModifiedDate)))) { + } + else { + if(!JS_ReadBytes(aReader, &lastModifiedDate, sizeof(lastModifiedDate))) { + NS_WARNING("Failed to read lastModifiedDate"); return false; } lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate); } - aRetval->lastModifiedDate = lastModifiedDate; nsCString name; - if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) { + if (!StructuredCloneReadString(aReader, name)) { return false; } - CopyUTF8toUTF16(name, aRetval->name); return true; } -class ValueDeserializationHelper -{ -public: - static bool - CreateAndWrapMutableFile(JSContext* aCx, - IDBDatabase* aDatabase, - StructuredCloneFile& aFile, - const MutableFileData& aData, - JS::MutableHandle aResult) - { - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aDatabase); - MOZ_ASSERT(aFile.mFileInfo); - - nsRefPtr mutableFile = - IDBMutableFile::Create(aDatabase, - aData.name, - aData.type, - aFile.mFileInfo.forget()); - MOZ_ASSERT(mutableFile); - - JS::Rooted result(aCx, mutableFile->WrapObject(aCx)); - if (NS_WARN_IF(!result)) { - return false; - } - - aResult.set(result); - return true; - } - - static bool - CreateAndWrapBlobOrFile(JSContext* aCx, - StructuredCloneFile& aFile, - const BlobOrFileData& aData, - JS::MutableHandle aResult) - { - MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || - aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || - aData.tag == SCTAG_DOM_BLOB); - MOZ_ASSERT(aFile.mFile); - - MOZ_ASSERT(NS_IsMainThread(), - "This wrapping currently only works on the main thread!"); - - if (aData.tag == SCTAG_DOM_BLOB) { - if (NS_WARN_IF(!ResolveMysteryBlob(aFile.mFile, - aData.type, - aData.size))) { - return false; - } - - JS::Rooted wrappedBlob(aCx); - nsresult rv = - nsContentUtils::WrapNative(aCx, - aFile.mFile, - &NS_GET_IID(nsIDOMBlob), - &wrappedBlob); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - aResult.set(&wrappedBlob.toObject()); - return true; - } - - nsCOMPtr domFile = do_QueryInterface(aFile.mFile); - MOZ_ASSERT(domFile); - - if (NS_WARN_IF(!ResolveMysteryFile(domFile, - aData.name, - aData.type, - aData.size, - aData.lastModifiedDate))) { - return false; - } - - JS::Rooted wrappedFile(aCx); - nsresult rv = - nsContentUtils::WrapNative(aCx, - aFile.mFile, - &NS_GET_IID(nsIDOMFile), - &wrappedFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - return false; - } - - aResult.set(&wrappedFile.toObject()); - return true; - } -}; - -class IndexDeserializationHelper -{ -public: - static bool - CreateAndWrapMutableFile(JSContext* aCx, - IDBDatabase* aDatabase, - StructuredCloneFile& aFile, - const MutableFileData& aData, - JS::MutableHandle aResult) - { - MOZ_ASSERT(!aDatabase); - - // MutableFile can't be used in index creation, so just make a dummy object. - JS::Rooted obj(aCx, - JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); - - if (NS_WARN_IF(!obj)) { - return false; - } - - aResult.set(obj); - return true; - } - - static bool - CreateAndWrapBlobOrFile(JSContext* aCx, - StructuredCloneFile& aFile, - const BlobOrFileData& aData, - JS::MutableHandle aResult) - { - MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || - aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || - aData.tag == SCTAG_DOM_BLOB); - - // The following properties are available for use in index creation - // Blob.size - // Blob.type - // File.name - // File.lastModifiedDate - - JS::Rooted obj(aCx, - JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); - if (NS_WARN_IF(!obj)) { - return false; - } - - // Technically these props go on the proto, but this detail won't change - // the results of index creation. - - JS::Rooted type(aCx, - JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length())); - if (NS_WARN_IF(!type)) { - return false; - } - - if (NS_WARN_IF(!JS_DefineProperty(aCx, - obj, - "size", - double(aData.size), - 0))) { - return false; - } - - if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "type", type, 0))) { - return false; - } - - if (aData.tag == SCTAG_DOM_BLOB) { - aResult.set(obj); - return true; - } - - JS::Rooted name(aCx, - JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length())); - if (NS_WARN_IF(!name)) { - return false; - } - - JS::Rooted date(aCx, - JS_NewDateObjectMsec(aCx, aData.lastModifiedDate)); - if (NS_WARN_IF(!date)) { - return false; - } - - if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "name", name, 0))) { - return false; - } - - if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0))) { - return false; - } - - aResult.set(obj); - return true; - } -}; - +// static template JSObject* -CommonStructuredCloneReadCallback(JSContext* aCx, - JSStructuredCloneReader* aReader, - uint32_t aTag, - uint32_t aData, - void* aClosure) +IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + uint32_t aData, + void* aClosure) { // We need to statically assert that our tag values are what we expect // so that if people accidentally change them they notice. - static_assert(SCTAG_DOM_BLOB == 0xffff8001 && - SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 && - SCTAG_DOM_MUTABLEFILE == 0xffff8004 && - SCTAG_DOM_FILE == 0xffff8005, + static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 && + SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 && + SCTAG_DOM_MUTABLEFILE == 0xFFFF8004 && + SCTAG_DOM_FILE == 0xFFFF8005, "You changed our structured clone tag values and just ate " "everyone's IndexedDB data. I hope you are happy."); if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || + aTag == SCTAG_DOM_MUTABLEFILE || aTag == SCTAG_DOM_BLOB || - aTag == SCTAG_DOM_FILE || - aTag == SCTAG_DOM_MUTABLEFILE) { - auto* cloneReadInfo = static_cast(aClosure); + aTag == SCTAG_DOM_FILE) { + StructuredCloneReadInfo* cloneReadInfo = + reinterpret_cast(aClosure); if (aData >= cloneReadInfo->mFiles.Length()) { - MOZ_ASSERT(false, "Bad index value!"); + NS_ERROR("Bad blob index!"); return nullptr; } StructuredCloneFile& file = cloneReadInfo->mFiles[aData]; - - JS::Rooted result(aCx); + IDBDatabase* database = cloneReadInfo->mDatabase; if (aTag == SCTAG_DOM_MUTABLEFILE) { MutableFileData data; - if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) { + if (!ReadMutableFile(aReader, &data)) { return nullptr; } + JS::Rooted result(aCx); if (NS_WARN_IF(!Traits::CreateAndWrapMutableFile(aCx, - cloneReadInfo->mDatabase, + database, file, data, &result))) { @@ -810,18 +1555,11 @@ CommonStructuredCloneReadCallback(JSContext* aCx, } BlobOrFileData data; - if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) { + if (!ReadBlobOrFile(aReader, aTag, &data)) { return nullptr; } - if (NS_WARN_IF(!Traits::CreateAndWrapBlobOrFile(aCx, - file, - data, - &result))) { - return nullptr; - } - - return result; + return Traits::CreateAndWrapBlobOrFile(aCx, database, file, data); } const JSStructuredCloneCallbacks* runtimeCallbacks = @@ -835,144 +1573,182 @@ CommonStructuredCloneReadCallback(JSContext* aCx, } // static -void -ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer) +bool +IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx, + JSStructuredCloneWriter* aWriter, + JS::Handle aObj, + void* aClosure) { - if (aBuffer.data()) { - aBuffer.clear(); + StructuredCloneWriteInfo* cloneWriteInfo = + reinterpret_cast(aClosure); + + if (JS_GetClass(aObj) == &sDummyPropJSClass) { + NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0, + "We should not have been here before!"); + cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter); + + uint64_t value = 0; + // Omit endian swap + return JS_WriteBytes(aWriter, &value, sizeof(value)); } -} -} // anonymous namespace + IDBTransaction* transaction = cloneWriteInfo->mTransaction; + FileManager* fileManager = transaction->Database()->Manager(); -const JSClass IDBObjectStore::sDummyPropJSClass = { - "IDBObjectStore Dummy", - 0 /* flags */, - JS_PropertyStub /* addProperty */, - JS_DeletePropertyStub /* delProperty */, - JS_PropertyStub /* getProperty */, - JS_StrictPropertyStub /* setProperty */, - JS_EnumerateStub /* enumerate */, - JS_ResolveStub /* resolve */, - JS_ConvertStub /* convert */, - JSCLASS_NO_OPTIONAL_MEMBERS -}; + IDBMutableFile* mutableFile = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) { + nsRefPtr fileInfo = mutableFile->GetFileInfo(); + MOZ_ASSERT(fileInfo); -IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction, - const ObjectStoreSpec* aSpec) - : mTransaction(aTransaction) - , mCachedKeyPath(JSVAL_VOID) - , mSpec(aSpec) - , mId(aSpec->metadata().id()) - , mRooted(false) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); - MOZ_ASSERT(aSpec); + // Throw when trying to store mutable files across databases. + if (fileInfo->Manager() != fileManager) { + return false; + } - SetIsDOMBinding(); -} + NS_ConvertUTF16toUTF8 convType(mutableFile->Type()); + uint32_t convTypeLength = + NativeEndian::swapToLittleEndian(convType.Length()); -IDBObjectStore::~IDBObjectStore() -{ - AssertIsOnOwningThread(); + NS_ConvertUTF16toUTF8 convName(mutableFile->Name()); + uint32_t convNameLength = + NativeEndian::swapToLittleEndian(convName.Length()); - if (mRooted) { - mCachedKeyPath = JSVAL_VOID; - mozilla::DropJSObjects(this); + if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, + cloneWriteInfo->mFiles.Length()) || + !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) || + !JS_WriteBytes(aWriter, convType.get(), convType.Length()) || + !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) || + !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { + return false; + } + + StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement(); + file->mFileInfo = fileInfo.forget(); + + return true; } -} -// static -already_AddRefed -IDBObjectStore::Create(IDBTransaction* aTransaction, - const ObjectStoreSpec& aSpec) -{ - MOZ_ASSERT(aTransaction); - aTransaction->AssertIsOnOwningThread(); + nsCOMPtr wrappedNative; + nsContentUtils::XPConnect()-> + GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); - nsRefPtr objectStore = - new IDBObjectStore(aTransaction, &aSpec); + if (wrappedNative) { + nsISupports* supports = wrappedNative->Native(); - return objectStore.forget(); + nsCOMPtr blob = do_QueryInterface(supports); + if (blob) { + nsCOMPtr inputStream; + + // Check if it is a blob created from this db or the blob was already + // stored in this db + nsRefPtr fileInfo = transaction->GetFileInfo(blob); + if (!fileInfo && fileManager) { + fileInfo = blob->GetFileInfo(fileManager); + + if (!fileInfo) { + fileInfo = fileManager->GetNewFileInfo(); + if (!fileInfo) { + NS_WARNING("Failed to get new file info!"); + return false; + } + + if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) { + NS_WARNING("Failed to get internal steam!"); + return false; + } + + transaction->AddFileInfo(blob, fileInfo); + } + } + + uint64_t size; + if (NS_FAILED(blob->GetSize(&size))) { + NS_WARNING("Failed to get size!"); + return false; + } + size = NativeEndian::swapToLittleEndian(size); + + nsString type; + if (NS_FAILED(blob->GetType(type))) { + NS_WARNING("Failed to get type!"); + return false; + } + NS_ConvertUTF16toUTF8 convType(type); + uint32_t convTypeLength = + NativeEndian::swapToLittleEndian(convType.Length()); + + nsCOMPtr file = do_QueryInterface(blob); + + if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, + cloneWriteInfo->mFiles.Length()) || + !JS_WriteBytes(aWriter, &size, sizeof(size)) || + !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) || + !JS_WriteBytes(aWriter, convType.get(), convType.Length())) { + return false; + } + + if (file) { + uint64_t lastModifiedDate = 0; + if (NS_FAILED(file->GetMozLastModifiedDate(&lastModifiedDate))) { + NS_WARNING("Failed to get last modified date!"); + return false; + } + + lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate); + + nsString name; + if (NS_FAILED(file->GetName(name))) { + NS_WARNING("Failed to get name!"); + return false; + } + NS_ConvertUTF16toUTF8 convName(name); + uint32_t convNameLength = + NativeEndian::swapToLittleEndian(convName.Length()); + + if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) || + !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) || + !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { + return false; + } + } + + StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement(); + cloneFile->mFile = blob.forget(); + cloneFile->mFileInfo = fileInfo.forget(); + cloneFile->mInputStream = inputStream.forget(); + + return true; + } + } + + // try using the runtime callbacks + const JSStructuredCloneCallbacks* runtimeCallbacks = + js::GetContextStructuredCloneCallbacks(aCx); + if (runtimeCallbacks) { + return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr); + } + + return false; } // static nsresult -IDBObjectStore::AppendIndexUpdateInfo( - int64_t aIndexID, - const KeyPath& aKeyPath, - bool aUnique, - bool aMultiEntry, - JSContext* aCx, - JS::Handle aVal, - nsTArray& aUpdateInfoArray) +IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds, + nsTArray& aResult) { - nsresult rv; + nsCharSeparatedTokenizerTemplate tokenizer(aFileIds, ' '); - if (!aMultiEntry) { - Key key; - rv = aKeyPath.ExtractKey(aCx, aVal, key); + while (tokenizer.hasMoreTokens()) { + nsString token(tokenizer.nextToken()); - // If an index's keyPath doesn't match an object, we ignore that object. - if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { - return NS_OK; - } + NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!"); - if (NS_FAILED(rv)) { - return rv; - } - - IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->indexId() = aIndexID; - updateInfo->value() = key; - - return NS_OK; - } - - JS::Rooted val(aCx); - if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) { - return NS_OK; - } - - if (JS_IsArrayObject(aCx, val)) { - JS::Rooted array(aCx, &val.toObject()); - uint32_t arrayLength; - if (NS_WARN_IF(!JS_GetArrayLength(aCx, array, &arrayLength))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { - JS::Rooted arrayItem(aCx); - if (NS_WARN_IF(!JS_GetElement(aCx, array, arrayIndex, &arrayItem))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - Key value; - if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || - value.IsUnset()) { - // Not a value we can do anything with, ignore it. - continue; - } - - IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->indexId() = aIndexID; - updateInfo->value() = value; - } - } - else { - Key value; - if (NS_FAILED(value.SetFromJSVal(aCx, val)) || - value.IsUnset()) { - // Not a value we can do anything with, ignore it. - return NS_OK; - } - - IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->indexId() = aIndexID; - updateInfo->value() = value; + nsresult rv; + int32_t id = token.ToInteger(&rv); + NS_ENSURE_SUCCESS(rv, rv); + + int64_t* element = aResult.AppendElement(); + *element = id; } return NS_OK; @@ -980,108 +1756,111 @@ IDBObjectStore::AppendIndexUpdateInfo( // static void -IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo) +IDBObjectStore::ConvertActorsToBlobs( + const InfallibleTArray& aActors, + nsTArray& aFiles) { - // This is kind of tricky, we only want to release stuff on the main thread, - // but we can end up being called on other threads if we have already been - // cleared on the main thread. - if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) { - return; + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!"); + + if (!aActors.IsEmpty()) { + NS_ASSERTION(ContentChild::GetSingleton(), "This should never be null!"); + + uint32_t length = aActors.Length(); + aFiles.SetCapacity(length); + + for (uint32_t index = 0; index < length; index++) { + BlobChild* actor = static_cast(aActors[index]); + + StructuredCloneFile* file = aFiles.AppendElement(); + file->mFile = actor->GetBlob(); + } + } +} + +// static +nsresult +IDBObjectStore::ConvertBlobsToActors( + nsIContentParent* aContentParent, + FileManager* aFileManager, + const nsTArray& aFiles, + InfallibleTArray& aActors) +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aContentParent, "Null contentParent!"); + NS_ASSERTION(aFileManager, "Null file manager!"); + + if (!aFiles.IsEmpty()) { + nsCOMPtr directory = aFileManager->GetDirectory(); + if (!directory) { + IDB_WARNING("Failed to get directory!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + uint32_t fileCount = aFiles.Length(); + aActors.SetCapacity(fileCount); + + for (uint32_t index = 0; index < fileCount; index++) { + const StructuredCloneFile& file = aFiles[index]; + NS_ASSERTION(file.mFileInfo, "This should never be null!"); + + nsCOMPtr nativeFile = + aFileManager->GetFileForId(directory, file.mFileInfo->Id()); + if (!nativeFile) { + IDB_WARNING("Failed to get file!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsCOMPtr blob = DOMFile::CreateFromFile(nativeFile, + file.mFileInfo); + + BlobParent* actor = + aContentParent->GetOrCreateActorForBlob(blob); + if (!actor) { + // This can only fail if the child has crashed. + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + aActors.AppendElement(actor); + } } - // If there's something to clear, we should be on the main thread. + return NS_OK; +} + +IDBObjectStore::IDBObjectStore() +: mId(INT64_MIN), + mKeyPath(0), + mCachedKeyPath(JSVAL_VOID), + mRooted(false), + mAutoIncrement(false), + mActorChild(nullptr), + mActorParent(nullptr) +{ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer); - aReadInfo.mFiles.Clear(); + SetIsDOMBinding(); } -// static -bool -IDBObjectStore::DeserializeValue(JSContext* aCx, - StructuredCloneReadInfo& aCloneReadInfo, - JS::MutableHandle aValue) +IDBObjectStore::~IDBObjectStore() { - MOZ_ASSERT(aCx); - - if (aCloneReadInfo.mData.IsEmpty()) { - aValue.setUndefined(); - return true; + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->Send__delete__(mActorChild); + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); } - auto* data = reinterpret_cast(aCloneReadInfo.mData.Elements()); - size_t dataLen = aCloneReadInfo.mData.Length(); - - MOZ_ASSERT(!(dataLen % sizeof(*data))); - - JSAutoRequest ar(aCx); - - static JSStructuredCloneCallbacks callbacks = { - CommonStructuredCloneReadCallback, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr - }; - - if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION, - aValue, &callbacks, &aCloneReadInfo)) { - return false; + if (mRooted) { + mCachedKeyPath = JSVAL_VOID; + mozilla::DropJSObjects(this); } - - return true; } -// static -bool -IDBObjectStore::DeserializeIndexValue(JSContext* aCx, - StructuredCloneReadInfo& aCloneReadInfo, - JS::MutableHandle aValue) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(aCx); - - if (aCloneReadInfo.mData.IsEmpty()) { - aValue.setUndefined(); - return true; - } - - size_t dataLen = aCloneReadInfo.mData.Length(); - - uint64_t* data = - const_cast(reinterpret_cast( - aCloneReadInfo.mData.Elements())); - - MOZ_ASSERT(!(dataLen % sizeof(*data))); - - JSAutoRequest ar(aCx); - - static JSStructuredCloneCallbacks callbacks = { - CommonStructuredCloneReadCallback, - nullptr, - nullptr - }; - - if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION, - aValue, &callbacks, &aCloneReadInfo)) { - return false; - } - - return true; -} - -#ifdef DEBUG - -void -IDBObjectStore::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mTransaction); - mTransaction->AssertIsOnOwningThread(); -} - -#endif // DEBUG - nsresult IDBObjectStore::GetAddInfo(JSContext* aCx, JS::Handle aValue, @@ -1090,15 +1869,15 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, Key& aKey, nsTArray& aUpdateInfoArray) { + nsresult rv; + // Return DATA_ERR if a key was passed in and this objectStore uses inline // keys. if (!aKeyVal.isUndefined() && HasValidKeyPath()) { return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } - bool isAutoIncrement = AutoIncrement(); - - nsresult rv; + JSAutoRequest ar(aCx); if (!HasValidKeyPath()) { // Out-of-line keys must be passed in. @@ -1106,7 +1885,8 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, if (NS_FAILED(rv)) { return rv; } - } else if (!isAutoIncrement) { + } + else if (!mAutoIncrement) { rv = GetKeyPath().ExtractKey(aCx, aValue, aKey); if (NS_FAILED(rv)) { return rv; @@ -1115,38 +1895,31 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, // Return DATA_ERR if no key was specified this isn't an autoIncrement // objectStore. - if (aKey.IsUnset() && !isAutoIncrement) { + if (aKey.IsUnset() && !mAutoIncrement) { return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } // Figure out indexes and the index values to update here. - const nsTArray& indexes = mSpec->indexes(); + uint32_t count = mInfo->indexes.Length(); + aUpdateInfoArray.SetCapacity(count); // Pretty good estimate + for (uint32_t indexesIndex = 0; indexesIndex < count; indexesIndex++) { + const IndexInfo& indexInfo = mInfo->indexes[indexesIndex]; - const uint32_t idxCount = indexes.Length(); - aUpdateInfoArray.SetCapacity(idxCount); // Pretty good estimate - - for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) { - const IndexMetadata& metadata = indexes[idxIndex]; - - rv = AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(), - metadata.unique(), metadata.multiEntry(), aCx, + rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath, + indexInfo.unique, indexInfo.multiEntry, aCx, aValue, aUpdateInfoArray); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); } - GetAddInfoClosure data(aCloneWriteInfo, aValue); + GetAddInfoClosure data = {this, aCloneWriteInfo, aValue}; - if (isAutoIncrement && HasValidKeyPath()) { - MOZ_ASSERT(aKey.IsUnset()); + if (mAutoIncrement && HasValidKeyPath()) { + NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!"); - rv = GetKeyPath().ExtractOrCreateKey(aCx, - aValue, - aKey, - &GetAddInfoCallback, - &data); - } else { + rv = GetKeyPath().ExtractOrCreateKey(aCx, aValue, aKey, + &GetAddInfoCallback, &data); + } + else { rv = GetAddInfoCallback(aCx, &data); } @@ -1154,126 +1927,48 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, } already_AddRefed -IDBObjectStore::AddOrPut(JSContext* aCx, - JS::Handle aValue, +IDBObjectStore::AddOrPut(JSContext* aCx, JS::Handle aValue, JS::Handle aKey, - bool aOverwrite, - ErrorResult& aRv) + bool aOverwrite, ErrorResult& aRv) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aCx); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!mTransaction->IsWriteAllowed()) { + if (!IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - JS::Rooted value(aCx, aValue); + StructuredCloneWriteInfo cloneWriteInfo; Key key; - StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database()); nsTArray updateInfo; + JS::Rooted value(aCx, aValue); aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo); if (aRv.Failed()) { return nullptr; } - FallibleTArray cloneData; - if (NS_WARN_IF(!cloneData.SetLength(cloneWriteInfo.mCloneBuffer.nbytes()))) { - aRv = NS_ERROR_OUT_OF_MEMORY; + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - // XXX Remove this - memcpy(cloneData.Elements(), cloneWriteInfo.mCloneBuffer.data(), - cloneWriteInfo.mCloneBuffer.nbytes()); + nsRefPtr helper = + new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key, + aOverwrite, updateInfo); - cloneWriteInfo.mCloneBuffer.clear(); - - ObjectStoreAddPutParams commonParams; - commonParams.objectStoreId() = Id(); - commonParams.cloneInfo().data().SwapElements(cloneData); - commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp; - commonParams.key() = key; - commonParams.indexUpdateInfos().SwapElements(updateInfo); - - // Convert any blobs or fileIds into DatabaseFileOrMutableFileId. - nsTArray& blobOrFileInfos = - cloneWriteInfo.mBlobOrFileInfos; - - FallibleTArray> fileInfosToKeepAlive; - - if (!blobOrFileInfos.IsEmpty()) { - const uint32_t count = blobOrFileInfos.Length(); - - FallibleTArray fileActorOrMutableFileIds; - if (NS_WARN_IF(!fileActorOrMutableFileIds.SetCapacity(count))) { - aRv = NS_ERROR_OUT_OF_MEMORY; - return nullptr; - } - - IDBDatabase* database = mTransaction->Database(); - - for (uint32_t index = 0; index < count; index++) { - StructuredCloneWriteInfo::BlobOrFileInfo& blobOrFileInfo = - blobOrFileInfos[index]; - MOZ_ASSERT((blobOrFileInfo.mBlob && !blobOrFileInfo.mFileInfo) || - (!blobOrFileInfo.mBlob && blobOrFileInfo.mFileInfo)); - - if (blobOrFileInfo.mBlob) { - PBackgroundIDBDatabaseFileChild* fileActor = - database->GetOrCreateFileActorForBlob(blobOrFileInfo.mBlob); - if (NS_WARN_IF(!fileActor)) { - IDB_REPORT_INTERNAL_ERR(); - aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - return nullptr; - } - - MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileActor)); - } else { - const int64_t fileId = blobOrFileInfo.mFileInfo->Id(); - MOZ_ASSERT(fileId > 0); - - MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileId)); - - nsRefPtr* newFileInfo = fileInfosToKeepAlive.AppendElement(); - if (NS_WARN_IF(!newFileInfo)) { - aRv = NS_ERROR_OUT_OF_MEMORY; - return nullptr; - } - - newFileInfo->swap(blobOrFileInfo.mFileInfo); - } - } - - commonParams.files().SwapElements(fileActorOrMutableFileIds); - } - - RequestParams params; - if (aOverwrite) { - params = ObjectStorePutParams(commonParams); - } else { - params = ObjectStoreAddParams(commonParams); - } - - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - mTransaction->StartRequest(actor, params); - - if (!fileInfosToKeepAlive.IsEmpty()) { - nsTArray> fileInfos; - fileInfosToKeepAlive.SwapElements(fileInfos); - - actor->HoldFileInfosUntilComplete(fileInfos); - MOZ_ASSERT(fileInfos.IsEmpty()); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } #ifdef IDB_PROFILER_USE_MARKS @@ -1286,7 +1981,8 @@ IDBObjectStore::AddOrPut(JSContext* aCx, IDB_PROFILER_STRING(Transaction()), IDB_PROFILER_STRING(this), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); - } else { + } + else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).add(%s)", "IDBRequest[%llu] MT IDBObjectStore.add()", @@ -1301,78 +1997,277 @@ IDBObjectStore::AddOrPut(JSContext* aCx, return request.forget(); } -already_AddRefed -IDBObjectStore::GetAllInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, - ErrorResult& aRv) +nsresult +IDBObjectStore::AddOrPutInternal( + const SerializedStructuredCloneWriteInfo& aCloneWriteInfo, + const Key& aKey, + const InfallibleTArray& aUpdateInfoArray, + const nsTArray >& aBlobs, + bool aOverwrite, + IDBRequest** _retval) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + if (!mTransaction->IsOpen()) { + return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; + } + + if (!IsWriteAllowed()) { + return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR; + } + + nsRefPtr request = GenerateRequest(this); + IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + StructuredCloneWriteInfo cloneWriteInfo; + if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) { + IDB_WARNING("Failed to copy structured clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!aBlobs.IsEmpty()) { + FileManager* fileManager = Transaction()->Database()->Manager(); + NS_ASSERTION(fileManager, "Null file manager?!"); + + uint32_t length = aBlobs.Length(); + cloneWriteInfo.mFiles.SetCapacity(length); + + for (uint32_t index = 0; index < length; index++) { + const nsCOMPtr& blob = aBlobs[index]; + + nsCOMPtr inputStream; + + nsRefPtr fileInfo = Transaction()->GetFileInfo(blob); + if (!fileInfo) { + fileInfo = blob->GetFileInfo(fileManager); + + if (!fileInfo) { + fileInfo = fileManager->GetNewFileInfo(); + if (!fileInfo) { + IDB_WARNING("Failed to get new file info!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) { + IDB_WARNING("Failed to get internal steam!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + // XXXbent This is where we should send a message back to the child to + // update the file id. + + Transaction()->AddFileInfo(blob, fileInfo); + } + } + + StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement(); + file->mFile = blob; + file->mFileInfo.swap(fileInfo); + file->mInputStream.swap(inputStream); + } + } + + Key key(aKey); + + nsTArray updateInfo(aUpdateInfoArray); + + nsRefPtr helper = + new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key, + aOverwrite, updateInfo); + + nsresult rv = helper->DispatchToTransactionPool(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + +#ifdef IDB_PROFILER_USE_MARKS + if (aOverwrite) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).%s(%s)", + "IDBRequest[%llu] MT IDBObjectStore.put()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), + key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); + } + else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).add(%s)", + "IDBRequest[%llu] MT IDBObjectStore.add()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), + key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); + } +#endif + + request.forget(_retval); + return NS_OK; +} + +already_AddRefed +IDBObjectStore::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aKeyRange, "Null pointer!"); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (NS_WARN_IF(aRv.Failed())) { + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } - const int64_t id = Id(); + nsRefPtr helper = + new GetHelper(mTransaction, request, this, aKeyRange); - OptionalKeyRange optionalKeyRange; - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - optionalKeyRange = serializedKeyRange; - } else { - optionalKeyRange = void_t(); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } - const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0; + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).get(%s)", + "IDBRequest[%llu] MT IDBObjectStore.get()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - RequestParams params; - if (aKeysOnly) { - params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit); - } else { - params = ObjectStoreGetAllParams(id, optionalKeyRange, limit); + return request.forget(); +} + +already_AddRefed +IDBObjectStore::GetAllInternal(IDBKeyRange* aKeyRange, + uint32_t aLimit, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; } nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - mTransaction->StartRequest(actor, params); - -#ifdef IDB_PROFILER_USE_MARKS - if (aKeysOnly) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "getAllKeys(%s, %lu)", - "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKeyRange), - aLimit); - } else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "getAll(%s, %lu)", - "IDBRequest[%llu] MT IDBObjectStore.getAll()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKeyRange), - aLimit); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } -#endif + + nsRefPtr helper = + new GetAllHelper(mTransaction, request, this, aKeyRange, aLimit); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "getAll(%s, %lu)", + "IDBRequest[%llu] MT IDBObjectStore.getAll()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + aLimit); + + return request.forget(); +} + +already_AddRefed +IDBObjectStore::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + nsRefPtr helper = + new GetAllKeysHelper(mTransaction, request, this, aKeyRange, aLimit); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "getAllKeys(%s, %lu)", + "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + aLimit); + + return request.forget(); +} + +already_AddRefed +IDBObjectStore::DeleteInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aKeyRange, "Null key range!"); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + if (!IsWriteAllowed()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); + return nullptr; + } + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + nsRefPtr helper = + new DeleteHelper(mTransaction, request, this, aKeyRange); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).delete(%s)", + "IDBRequest[%llu] MT IDBObjectStore.delete()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); return request.forget(); } @@ -1380,27 +2275,33 @@ IDBObjectStore::GetAllInternal(bool aKeysOnly, already_AddRefed IDBObjectStore::Clear(ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!mTransaction->IsWriteAllowed()) { + if (!IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - ObjectStoreClearParams params; - params.objectStoreId() = Id(); - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } - BackgroundRequestChild* actor = new BackgroundRequestChild(request); + nsRefPtr helper(new ClearHelper(mTransaction, request, this)); - mTransaction->StartRequest(actor, params); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).clear()", @@ -1413,58 +2314,290 @@ IDBObjectStore::Clear(ErrorResult& aRv) return request.forget(); } -already_AddRefed -IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv) +already_AddRefed +IDBObjectStore::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (mTransaction->IsFinished()) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - const nsTArray& indexes = mSpec->indexes(); + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } - const IndexMetadata* metadata = nullptr; + nsRefPtr helper = + new CountHelper(mTransaction, request, this, aKeyRange); + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } - for (uint32_t idxCount = indexes.Length(), idxIndex = 0; - idxIndex < idxCount; - idxIndex++) { - const IndexMetadata& index = indexes[idxIndex]; - if (index.name() == aName) { - metadata = &index; + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).count(%s)", + "IDBRequest[%llu] MT IDBObjectStore.count()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + + return request.forget(); +} + +already_AddRefed +IDBObjectStore::OpenCursorInternal(IDBKeyRange* aKeyRange, + size_t aDirection, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + IDBCursor::Direction direction = + static_cast(aDirection); + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + nsRefPtr helper = + new OpenCursorHelper(mTransaction, request, this, aKeyRange, direction); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "openCursor(%s, %s)", + "IDBRequest[%llu] MT IDBObjectStore.openCursor()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + IDB_PROFILER_STRING(direction)); + + return request.forget(); +} + +nsresult +IDBObjectStore::OpenCursorFromChildProcess( + IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + const SerializedStructuredCloneReadInfo& aCloneInfo, + nsTArray& aBlobs, + IDBCursor** _retval) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) || + (aCloneInfo.dataLength && aCloneInfo.data), + "Inconsistent clone info!"); + + IDBCursor::Direction direction = + static_cast(aDirection); + + StructuredCloneReadInfo cloneInfo; + + if (!cloneInfo.SetFromSerialized(aCloneInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + cloneInfo.mFiles.SwapElements(aBlobs); + + nsRefPtr cursor = + IDBCursor::Create(aRequest, mTransaction, this, direction, Key(), + EmptyCString(), EmptyCString(), aKey, Move(cloneInfo)); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!"); + + cursor.forget(_retval); + return NS_OK; +} + +nsresult +IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + IDBCursor** _retval) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aRequest); + + auto direction = static_cast(aDirection); + + nsRefPtr cursor = + IDBCursor::Create(aRequest, mTransaction, this, direction, Key(), + EmptyCString(), EmptyCString(), aKey); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + cursor.forget(_retval); + return NS_OK; +} + +already_AddRefed +IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr request = GenerateRequest(this); + if (!request) { + IDB_WARNING("Failed to generate request!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + auto direction = static_cast(aDirection); + + nsRefPtr helper = + new OpenKeyCursorHelper(mTransaction, request, this, aKeyRange, direction); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "openKeyCursor(%s, %s)", + "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + IDB_PROFILER_STRING(direction)); + + return request.forget(); +} + +void +IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); + NS_ASSERTION(aInfo != mInfo, "This is nonsense"); + + mInfo = aInfo; +} + +already_AddRefed +IDBObjectStore::CreateIndexInternal(const IndexInfo& aInfo, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + IndexInfo* indexInfo = mInfo->indexes.AppendElement(); + + indexInfo->name = aInfo.name; + indexInfo->id = aInfo.id; + indexInfo->keyPath = aInfo.keyPath; + indexInfo->unique = aInfo.unique; + indexInfo->multiEntry = aInfo.multiEntry; + + // Don't leave this in the list if we fail below! + AutoRemoveIndex autoRemove(mInfo, aInfo.name); + + nsRefPtr index = IDBIndex::Create(this, indexInfo, true); + + mCreatedIndexes.AppendElement(index); + + if (IndexedDatabaseManager::IsMainProcess()) { + nsRefPtr helper = + new CreateIndexHelper(mTransaction, index); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + } + + autoRemove.forget(); + + IDB_PROFILER_MARK("IndexedDB Pseudo-request: " + "database(%s).transaction(%s).objectStore(%s)." + "createIndex(%s)", + "MT IDBObjectStore.createIndex()", + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index)); + + return index.forget(); +} + +already_AddRefed +IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (mTransaction->IsFinished()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + IndexInfo* indexInfo = nullptr; + uint32_t indexCount = mInfo->indexes.Length(); + for (uint32_t index = 0; index < indexCount; index++) { + if (mInfo->indexes[index].name == aName) { + indexInfo = &(mInfo->indexes[index]); break; } } - if (!metadata) { + if (!indexInfo) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return nullptr; } - const int64_t desiredId = metadata->id(); - - nsRefPtr index; - - for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0; - idxIndex < idxCount; - idxIndex++) { - nsRefPtr& existingIndex = mIndexes[idxIndex]; - - if (existingIndex->Id() == desiredId) { - index = existingIndex; + nsRefPtr retval; + for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) { + nsRefPtr& index = mCreatedIndexes[i]; + if (index->Name() == aName) { + retval = index; break; } } - if (!index) { - index = IDBIndex::Create(this, *metadata); - MOZ_ASSERT(index); + if (!retval) { + retval = IDBIndex::Create(this, indexInfo, false); + if (!retval) { + IDB_WARNING("Failed to create index!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } - mIndexes.AppendElement(index); + if (!mCreatedIndexes.AppendElement(retval)) { + IDB_WARNING("Out of memory!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } } - return index.forget(); + return retval.forget(); } NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore) @@ -1477,7 +2610,11 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes); + + for (uint32_t i = 0; i < tmp->mCreatedIndexes.Length(); i++) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]"); + cb.NoteXPCOMChild(static_cast(tmp->mCreatedIndexes[i].get())); + } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) @@ -1485,7 +2622,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) // Don't unlink mTransaction! - NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes); + tmp->mCreatedIndexes.Clear(); tmp->mCachedKeyPath = JSVAL_VOID; @@ -1509,15 +2646,8 @@ IDBObjectStore::WrapObject(JSContext* aCx) return IDBObjectStoreBinding::Wrap(aCx, this); } -nsPIDOMWindow* -IDBObjectStore::GetParentObject() const -{ - return mTransaction->GetParentObject(); -} - void -IDBObjectStore::GetKeyPath(JSContext* aCx, - JS::MutableHandle aResult, +IDBObjectStore::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -1543,32 +2673,28 @@ IDBObjectStore::GetKeyPath(JSContext* aCx, } already_AddRefed -IDBObjectStore::IndexNames() +IDBObjectStore::GetIndexNames(ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - const nsTArray& indexes = mSpec->indexes(); + nsRefPtr list(new DOMStringList()); - nsRefPtr list = new DOMStringList(); + nsTArray& names = list->StringArray(); + uint32_t count = mInfo->indexes.Length(); + names.SetCapacity(count); - if (!indexes.IsEmpty()) { - nsTArray& listNames = list->StringArray(); - listNames.SetCapacity(indexes.Length()); - - for (uint32_t index = 0; index < indexes.Length(); index++) { - listNames.InsertElementSorted(indexes[index].name()); - } + for (uint32_t index = 0; index < count; index++) { + names.InsertElementSorted(mInfo->indexes[index].name); } return list.forget(); } already_AddRefed -IDBObjectStore::Get(JSContext* aCx, - JS::Handle aKey, +IDBObjectStore::Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); @@ -1577,9 +2703,7 @@ IDBObjectStore::Get(JSContext* aCx, nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (aRv.Failed()) { - return nullptr; - } + ENSURE_SUCCESS(aRv, nullptr); if (!keyRange) { // Must specify a key or keyRange for get(). @@ -1587,50 +2711,52 @@ IDBObjectStore::Get(JSContext* aCx, return nullptr; } - ObjectStoreGetParams params; - params.objectStoreId() = Id(); - keyRange->ToSerialized(params.keyRange()); - - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - mTransaction->StartRequest(actor, params); - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).get(%s)", - "IDBRequest[%llu] MT IDBObjectStore.get()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - - return request.forget(); + return GetInternal(keyRange, aRv); } already_AddRefed -IDBObjectStore::Delete(JSContext* aCx, +IDBObjectStore::GetAll(JSContext* aCx, JS::Handle aKey, - ErrorResult& aRv) + const Optional& aLimit, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!mTransaction->IsWriteAllowed()) { + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + uint32_t limit = UINT32_MAX; + if (aLimit.WasPassed() && aLimit.Value() != 0) { + limit = aLimit.Value(); + } + + return GetAllInternal(keyRange, limit, aRv); +} + +already_AddRefed +IDBObjectStore::Delete(JSContext* aCx, JS::Handle aKey, + ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + if (!IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (NS_WARN_IF((aRv.Failed()))) { - return nullptr; - } + ENSURE_SUCCESS(aRv, nullptr); if (!keyRange) { // Must specify a key or keyRange for delete(). @@ -1638,36 +2764,38 @@ IDBObjectStore::Delete(JSContext* aCx, return nullptr; } - ObjectStoreDeleteParams params; - params.objectStoreId() = Id(); - keyRange->ToSerialized(params.keyRange()); + return DeleteInternal(keyRange, aRv); +} - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); +already_AddRefed +IDBObjectStore::OpenCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - BackgroundRequestChild* actor = new BackgroundRequestChild(request); + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } - mTransaction->StartRequest(actor, params); + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).delete(%s)", - "IDBRequest[%llu] MT IDBObjectStore.delete()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); + size_t argDirection = static_cast(direction); - return request.forget(); + return OpenCursorInternal(keyRange, argDirection, aRv); } already_AddRefed -IDBObjectStore::CreateIndex(JSContext* aCx, - const nsAString& aName, +IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, const nsAString& aKeyPath, const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); KeyPath keyPath(0); if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) || @@ -1676,40 +2804,39 @@ IDBObjectStore::CreateIndex(JSContext* aCx, return nullptr; } - return CreateIndexInternal(aCx, aName, keyPath, aOptionalParameters, aRv); + return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv); } already_AddRefed -IDBObjectStore::CreateIndex(JSContext* aCx, - const nsAString& aName, +IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, const Sequence& aKeyPath, const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!"); - KeyPath keyPath(0); - if (aKeyPath.IsEmpty() || - NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) || - !keyPath.IsValid()) { + if (!aKeyPath.Length()) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } - return CreateIndexInternal(aCx, aName, keyPath, aOptionalParameters, aRv); + KeyPath keyPath(0); + if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath))) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + + return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv); } already_AddRefed -IDBObjectStore::CreateIndexInternal( - JSContext* aCx, - const nsAString& aName, - const KeyPath& aKeyPath, - const IDBIndexParameters& aOptionalParameters, - ErrorResult& aRv) +IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, + KeyPath& aKeyPath, + const IDBIndexParameters& aOptionalParameters, + ErrorResult& aRv) { - AssertIsOnOwningThread(); - - IDBTransaction* transaction = IDBTransaction::GetCurrent(); + // Check name and current mode + IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); if (!transaction || transaction != mTransaction || @@ -1718,71 +2845,54 @@ IDBObjectStore::CreateIndexInternal( return nullptr; } - MOZ_ASSERT(transaction->IsOpen()); - - auto& indexes = const_cast&>(mSpec->indexes()); - for (uint32_t count = indexes.Length(), index = 0; - index < count; - index++) { - if (aName == indexes[index].name()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); - return nullptr; + bool found = false; + uint32_t indexCount = mInfo->indexes.Length(); + for (uint32_t index = 0; index < indexCount; index++) { + if (mInfo->indexes[index].name == aName) { + found = true; + break; } } + if (found) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); + return nullptr; + } + + NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); + +#ifdef DEBUG + for (uint32_t index = 0; index < mCreatedIndexes.Length(); index++) { + if (mCreatedIndexes[index]->Name() == aName) { + NS_ERROR("Already created this one!"); + } + } +#endif + if (aOptionalParameters.mMultiEntry && aKeyPath.IsArray()) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return nullptr; } -#ifdef DEBUG - for (uint32_t count = mIndexes.Length(), index = 0; - index < count; - index++) { - MOZ_ASSERT(mIndexes[index]->Name() != aName); - } -#endif + DatabaseInfo* databaseInfo = mTransaction->DBInfo(); - const IndexMetadata* oldMetadataElements = - indexes.IsEmpty() ? nullptr : indexes.Elements(); + IndexInfo info; - IndexMetadata* metadata = indexes.AppendElement( - IndexMetadata(transaction->NextIndexId(), nsString(aName), aKeyPath, - aOptionalParameters.mUnique, - aOptionalParameters.mMultiEntry)); + info.name = aName; + info.id = databaseInfo->nextIndexId++; + info.keyPath = aKeyPath; + info.unique = aOptionalParameters.mUnique; + info.multiEntry = aOptionalParameters.mMultiEntry; - if (oldMetadataElements && - oldMetadataElements != indexes.Elements()) { - MOZ_ASSERT(indexes.Length() > 1); - - // Array got moved, update the spec pointers for all live indexes. - RefreshSpec(/* aMayDelete */ false); - } - - transaction->CreateIndex(this, *metadata); - - nsRefPtr index = IDBIndex::Create(this, *metadata); - MOZ_ASSERT(index); - - mIndexes.AppendElement(index); - - IDB_PROFILER_MARK("IndexedDB Pseudo-request: " - "database(%s).transaction(%s).objectStore(%s)." - "createIndex(%s)", - "MT IDBObjectStore.createIndex()", - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index)); - - return index.forget(); + return CreateIndexInternal(info, aRv); } void IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - IDBTransaction* transaction = IDBTransaction::GetCurrent(); + IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); if (!transaction || transaction != mTransaction || @@ -1791,47 +2901,45 @@ IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) return; } - MOZ_ASSERT(transaction->IsOpen()); + NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); - auto& metadataArray = const_cast&>(mSpec->indexes()); - - int64_t foundId = 0; - - for (uint32_t metadataCount = metadataArray.Length(), metadataIndex = 0; - metadataIndex < metadataCount; - metadataIndex++) { - const IndexMetadata& metadata = metadataArray[metadataIndex]; - MOZ_ASSERT(metadata.id()); - - if (aName == metadata.name()) { - foundId = metadata.id(); - - // Must do this before altering the metadata array! - for (uint32_t indexCount = mIndexes.Length(), indexIndex = 0; - indexIndex < indexCount; - indexIndex++) { - nsRefPtr& index = mIndexes[indexIndex]; - - if (index->Id() == foundId) { - index->NoteDeletion(); - mIndexes.RemoveElementAt(indexIndex); - break; - } - } - - metadataArray.RemoveElementAt(metadataIndex); - - RefreshSpec(/* aMayDelete */ false); + uint32_t index = 0; + for (; index < mInfo->indexes.Length(); index++) { + if (mInfo->indexes[index].name == aName) { break; } } - if (!foundId) { + if (index == mInfo->indexes.Length()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return; } - transaction->DeleteIndex(this, foundId); + if (IndexedDatabaseManager::IsMainProcess()) { + nsRefPtr helper = + new DeleteIndexHelper(mTransaction, this, aName); + + nsresult rv = helper->DispatchToTransactionPool(); + if (NS_FAILED(rv)) { + IDB_WARNING("Failed to dispatch!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return; + } + } + else { + NS_ASSERTION(mActorChild, "Must have an actor here!"); + + mActorChild->SendDeleteIndex(nsString(aName)); + } + + mInfo->indexes.RemoveElementAt(index); + + for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) { + if (mCreatedIndexes[i]->Name() == aName) { + mCreatedIndexes.RemoveElementAt(i); + break; + } + } IDB_PROFILER_MARK("IndexedDB Pseudo-request: " "database(%s).transaction(%s).objectStore(%s)." @@ -1855,47 +2963,41 @@ IDBObjectStore::Count(JSContext* aCx, nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - if (aRv.Failed()) { - return nullptr; - } + ENSURE_SUCCESS(aRv, nullptr); - ObjectStoreCountParams params; - params.objectStoreId() = Id(); - - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - params.optionalKeyRange() = serializedKeyRange; - } else { - params.optionalKeyRange() = void_t(); - } - - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundRequestChild* actor = new BackgroundRequestChild(request); - - mTransaction->StartRequest(actor, params); - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).count(%s)", - "IDBRequest[%llu] MT IDBObjectStore.count()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - - return request.forget(); + return CountInternal(keyRange, aRv); } already_AddRefed -IDBObjectStore::OpenCursorInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv) +IDBObjectStore::GetAllKeys(JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv) { - AssertIsOnOwningThread(); + MOZ_ASSERT(NS_IsMainThread()); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + ENSURE_SUCCESS(aRv, nullptr); + + uint32_t limit = UINT32_MAX; + if (aLimit.WasPassed() && aLimit.Value() != 0) { + limit = aLimit.Value(); + } + + return GetAllKeysInternal(keyRange, limit, aRv); +} + +already_AddRefed +IDBObjectStore::OpenKeyCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); @@ -1904,190 +3006,2231 @@ IDBObjectStore::OpenCursorInternal(bool aKeysOnly, nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - int64_t objectStoreId = Id(); - - OptionalKeyRange optionalKeyRange; - - if (keyRange) { - SerializedKeyRange serializedKeyRange; - keyRange->ToSerialized(serializedKeyRange); - - optionalKeyRange = Move(serializedKeyRange); - } else { - optionalKeyRange = void_t(); - } + ENSURE_SUCCESS(aRv, nullptr); IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); - OpenCursorParams params; - if (aKeysOnly) { - ObjectStoreOpenKeyCursorParams openParams; - openParams.objectStoreId() = objectStoreId; - openParams.optionalKeyRange() = Move(optionalKeyRange); - openParams.direction() = direction; - - params = Move(openParams); - } else { - ObjectStoreOpenCursorParams openParams; - openParams.objectStoreId() = objectStoreId; - openParams.optionalKeyRange() = Move(optionalKeyRange); - openParams.direction() = direction; - - params = Move(openParams); - } - - nsRefPtr request = GenerateRequest(this); - MOZ_ASSERT(request); - - BackgroundCursorChild* actor = - new BackgroundCursorChild(request, this, direction); - - mTransaction->OpenCursor(actor, params); - -#ifdef IDB_PROFILER_USE_MARKS - if (aKeysOnly) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "openKeyCursor(%s, %s)", - "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - IDB_PROFILER_STRING(direction)); - } else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "openCursor(%s, %s)", - "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - IDB_PROFILER_STRING(direction)); - } -#endif - return request.forget(); + return OpenKeyCursorInternal(keyRange, static_cast(direction), aRv); } -void -IDBObjectStore::RefreshSpec(bool aMayDelete) +inline nsresult +CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream) { - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - const DatabaseSpec* dbSpec = mTransaction->Database()->Spec(); - MOZ_ASSERT(dbSpec); + PROFILER_LABEL("IDBObjectStore", "CopyData", + js::ProfileEntry::Category::STORAGE); - const nsTArray& objectStores = dbSpec->objectStores(); + nsresult rv; - bool found = false; + do { + char copyBuffer[FILE_COPY_BUFFER_SIZE]; - for (uint32_t objCount = objectStores.Length(), objIndex = 0; - objIndex < objCount; - objIndex++) { - const ObjectStoreSpec& objSpec = objectStores[objIndex]; + uint32_t numRead; + rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead); + NS_ENSURE_SUCCESS(rv, rv); - if (objSpec.metadata().id() == Id()) { - mSpec = &objSpec; - - for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0; - idxIndex < idxCount; - idxIndex++) { - mIndexes[idxIndex]->RefreshMetadata(aMayDelete); - } - - found = true; + if (!numRead) { break; } - } - MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, found); + uint32_t numWrite; + rv = aOutputStream->Write(copyBuffer, numRead, &numWrite); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + NS_ENSURE_SUCCESS(rv, rv); - if (found) { - MOZ_ASSERT(mSpec != mDeletedSpec); - mDeletedSpec = nullptr; - } else { - NoteDeletion(); - } -} + NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE); + } while (true); -const ObjectStoreSpec& -IDBObjectStore::Spec() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); + rv = aOutputStream->Flush(); + NS_ENSURE_SUCCESS(rv, rv); - return *mSpec; + return NS_OK; } void -IDBObjectStore::NoteDeletion() +ObjectStoreHelper::ReleaseMainThreadObjects() { - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); - MOZ_ASSERT(Id() == mSpec->metadata().id()); + mObjectStore = nullptr; + AsyncConnectionHelper::ReleaseMainThreadObjects(); +} - if (mDeletedSpec) { - MOZ_ASSERT(mDeletedSpec == mSpec); - return; +nsresult +ObjectStoreHelper::Dispatch(nsIEventTarget* aDatabaseThread) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("ObjectStoreHelper", "Dispatch", + js::ProfileEntry::Category::STORAGE); + + if (IndexedDatabaseManager::IsMainProcess()) { + return AsyncConnectionHelper::Dispatch(aDatabaseThread); } - // Copy the spec here. - mDeletedSpec = new ObjectStoreSpec(*mSpec); - mDeletedSpec->indexes().Clear(); + // If we've been invalidated then there's no point sending anything to the + // parent process. + if (mObjectStore->Transaction()->Database()->IsInvalidated()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - mSpec = mDeletedSpec; + IndexedDBObjectStoreChild* objectStoreActor = mObjectStore->GetActorChild(); + NS_ASSERTION(objectStoreActor, "Must have an actor here!"); - if (!mIndexes.IsEmpty()) { - for (uint32_t count = mIndexes.Length(), index = 0; - index < count; - index++) { - mIndexes[index]->NoteDeletion(); + ObjectStoreRequestParams params; + + // Our "parent" process may be either the root process or another content + // process if this indexedDB is managed by a PBrowser that is managed by a + // PContentBridge. We need to find which one it is so that we can create + // PBlobs that are managed by the right nsIContentChild. + IndexedDBChild* rootActor = + static_cast(objectStoreActor->Manager()-> + Manager()->Manager()); + nsIContentChild* blobCreator; + if (rootActor->GetManagerContent()) { + blobCreator = rootActor->GetManagerContent(); + } else { + blobCreator = rootActor->GetManagerTab()->Manager(); + } + + nsresult rv = PackArgumentsForParentProcess(params, blobCreator); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NoDispatchEventTarget target; + rv = AsyncConnectionHelper::Dispatch(&target); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mActor = + new IndexedDBObjectStoreRequestChild(this, mObjectStore, params.type()); + objectStoreActor->SendPIndexedDBRequestConstructor(mActor, params); + + return NS_OK; +} + +void +NoRequestObjectStoreHelper::ReleaseMainThreadObjects() +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mObjectStore = nullptr; + AsyncConnectionHelper::ReleaseMainThreadObjects(); +} + +nsresult +NoRequestObjectStoreHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + MOZ_CRASH(); +} + +AsyncConnectionHelper::ChildProcessSendResult +NoRequestObjectStoreHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + return Success_NotSent; +} + +nsresult +NoRequestObjectStoreHelper::OnSuccess() +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + return NS_OK; +} + +void +NoRequestObjectStoreHelper::OnError() +{ + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mTransaction->Abort(GetResultCode()); +} + +// This is a duplicate of the js engine's byte munging in StructuredClone.cpp +uint64_t +ReinterpretDoubleAsUInt64(double d) +{ + union { + double d; + uint64_t u; + } pun; + pun.d = d; + return pun.u; +} + +nsresult +AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aConnection, "Passed a null connection!"); + + PROFILER_LABEL("AddHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { + NS_WARNING("Refusing to add more data because disk space is low!"); + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + + nsresult rv; + bool keyUnset = mKey.IsUnset(); + int64_t osid = mObjectStore->Id(); + const KeyPath& keyPath = mObjectStore->GetKeyPath(); + + // The "|| keyUnset" here is mostly a debugging tool. If a key isn't + // specified we should never have a collision and so it shouldn't matter + // if we allow overwrite or not. By not allowing overwrite we raise + // detectable errors rather than corrupting data + nsCOMPtr stmt = !mOverwrite || keyUnset ? + mTransaction->GetCachedStatement( + "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)") : + mTransaction->GetCachedStatement( + "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, " + "file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)"); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(), + "Should have key unless autoincrement"); + + int64_t autoIncrementNum = 0; + + if (mObjectStore->IsAutoIncrement()) { + if (keyUnset) { + autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId; + + MOZ_ASSERT(autoIncrementNum > 0, + "Generated key must always be a positive integer"); + + if (autoIncrementNum > (1LL << 53)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mKey.SetFromInteger(autoIncrementNum); + } + else if (mKey.IsFloat() && + mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) { + autoIncrementNum = floor(mKey.ToFloat()); + } + + if (keyUnset && keyPath.IsValid()) { + // Special case where someone put an object into an autoIncrement'ing + // objectStore with no key in its keyPath set. We needed to figure out + // which row id we would get above before we could set that properly. + + LittleEndian::writeUint64((char*)mCloneWriteInfo.mCloneBuffer.data() + + mCloneWriteInfo.mOffsetToKeyProp, + ReinterpretDoubleAsUInt64(static_cast( + autoIncrementNum))); } } + + mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); + + + // Compress the bytes before adding into the database. + const char* uncompressed = + reinterpret_cast(mCloneWriteInfo.mCloneBuffer.data()); + size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes(); + + // We don't have a smart pointer class that calls moz_free, so we need to + // manage | compressed | manually. + { + size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); + // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage + // expects to be able to free the adopted pointer with NS_Free. + char* compressed = (char*)moz_malloc(compressedLength); + NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY); + + snappy::RawCompress(uncompressed, uncompressedLength, compressed, + &compressedLength); + + uint8_t* dataBuffer = reinterpret_cast(compressed); + size_t dataBufferLength = compressedLength; + + // If this call succeeds, | compressed | is now owned by the statement, and + // we are no longer responsible for it. + rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, + dataBufferLength); + if (NS_FAILED(rv)) { + moz_free(compressed); + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + // Handle blobs + uint32_t length = mCloneWriteInfo.mFiles.Length(); + if (length) { + nsRefPtr fileManager = mDatabase->Manager(); + + nsCOMPtr directory = fileManager->GetDirectory(); + IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr journalDirectory = fileManager->EnsureJournalDirectory(); + IDB_ENSURE_TRUE(journalDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsAutoString fileIds; + + for (uint32_t index = 0; index < length; index++) { + StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index]; + + FileInfo* fileInfo = cloneFile.mFileInfo; + nsIInputStream* inputStream = cloneFile.mInputStream; + + int64_t id = fileInfo->Id(); + if (inputStream) { + // Create a journal file first + nsCOMPtr nativeFile = + fileManager->GetFileForId(journalDirectory, id); + IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + // Now we can copy the blob + nativeFile = fileManager->GetFileForId(directory, id); + IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + IDBDatabase* database = mObjectStore->Transaction()->Database(); + nsRefPtr outputStream = + FileOutputStream::Create(database->Type(), database->Group(), + database->Origin(), nativeFile); + IDB_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = CopyData(inputStream, outputStream); + if (NS_FAILED(rv) && + NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { + IDB_REPORT_INTERNAL_ERR(); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + NS_ENSURE_SUCCESS(rv, rv); + + cloneFile.mFile->AddFileInfo(fileInfo); + } + + if (index) { + fileIds.Append(' '); + } + fileIds.AppendInt(id); + } + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); + } + else { + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->Execute(); + if (rv == NS_ERROR_STORAGE_CONSTRAINT) { + NS_ASSERTION(!keyUnset, "Generated key had a collision!?"); + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + int64_t objectDataId; + rv = aConnection->GetLastInsertRowID(&objectDataId); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + // Update our indexes if needed. + if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) { + rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite, + objectDataId, mIndexUpdateInfo); + if (rv == NS_ERROR_STORAGE_CONSTRAINT) { + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + if (autoIncrementNum) { + mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1; + } + + return NS_OK; } -const nsString& -IDBObjectStore::Name() const +nsresult +AddHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) { - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); + NS_ASSERTION(!mKey.IsUnset(), "Badness!"); - return mSpec->metadata().name(); + mCloneWriteInfo.mCloneBuffer.clear(); + + return mKey.ToJSVal(aCx, aVal); } -bool -IDBObjectStore::AutoIncrement() const +void +AddHelper::ReleaseMainThreadObjects() { - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); - - return mSpec->metadata().autoIncrement(); + IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); + ObjectStoreHelper::ReleaseMainThreadObjects(); } -const KeyPath& -IDBObjectStore::GetKeyPath() const +nsresult +AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) { - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - return mSpec->metadata().keyPath(); + PROFILER_MAIN_THREAD_LABEL("AddHelper", "PackArgumentsForParentProcess", + js::ProfileEntry::Category::STORAGE); + + AddPutParams commonParams; + commonParams.cloneInfo() = mCloneWriteInfo; + commonParams.key() = mKey; + commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo); + + const nsTArray& files = mCloneWriteInfo.mFiles; + + if (!files.IsEmpty()) { + uint32_t fileCount = files.Length(); + + InfallibleTArray& blobsChild = commonParams.blobsChild(); + blobsChild.SetCapacity(fileCount); + + NS_ASSERTION(aBlobCreator, "This should never be null!"); + + for (uint32_t index = 0; index < fileCount; index++) { + const StructuredCloneFile& file = files[index]; + + NS_ASSERTION(file.mFile, "This should never be null!"); + NS_ASSERTION(!file.mFileInfo, "This is not yet supported!"); + + BlobChild* actor = + aBlobCreator->GetOrCreateActorForBlob(file.mFile); + if (!actor) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + blobsChild.AppendElement(actor); + } + } + + if (mOverwrite) { + PutParams putParams; + putParams.commonParams() = commonParams; + aParams = putParams; + } + else { + AddParams addParams; + addParams.commonParams() = commonParams; + aParams = addParams; + } + + return NS_OK; } -bool -IDBObjectStore::HasValidKeyPath() const +AsyncConnectionHelper::ChildProcessSendResult +AddHelper::SendResponseToChildProcess(nsresult aResultCode) { - AssertIsOnOwningThread(); - MOZ_ASSERT(mSpec); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - return GetKeyPath().IsValid(); + PROFILER_MAIN_THREAD_LABEL("AddHelper", "SendResponseToChildProcess", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else if (mOverwrite) { + PutResponse putResponse; + putResponse.key() = mKey; + response = putResponse; + } + else { + AddResponse addResponse; + addResponse.key() = mKey; + response = addResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +nsresult +AddHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TAddResponse || + aResponseValue.type() == ResponseValue::TPutResponse, + "Bad response type!"); + + mKey = mOverwrite ? + aResponseValue.get_PutResponse().key() : + aResponseValue.get_AddResponse().key(); + + return NS_OK; +} + +nsresult +GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "Must have a key range here!"); + + PROFILER_LABEL("GetHelper", "DoDatabaseWork [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsCString keyRangeClause; + mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); + + NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); + + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1"); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = + stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (hasResult) { + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, + mDatabase, mCloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +GetHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal); + + mCloneReadInfo.mCloneBuffer.clear(); + + NS_ENSURE_TRUE(result, NS_ERROR_DOM_DATA_CLONE_ERR); + return NS_OK; +} + +void +GetHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + ObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +GetHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "This should never be null!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("GetHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + GetParams params; + + mKeyRange->ToSerializedKeyRange(params.keyRange()); + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("GetHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + InfallibleTArray blobsParent; + + if (NS_SUCCEEDED(aResultCode)) { + IDBDatabase* database = mObjectStore->Transaction()->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + const nsTArray& files = mCloneReadInfo.mFiles; + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobsParent); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobsToActors failed!"); + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + GetResponse getResponse; + getResponse.cloneInfo() = mCloneReadInfo; + getResponse.blobsParent().SwapElements(blobsParent); + response = getResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse, + "Bad response type!"); + + const GetResponse& getResponse = aResponseValue.get_GetResponse(); + const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo(); + + NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || + (cloneInfo.dataLength && cloneInfo.data), + "Inconsistent clone info!"); + + if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(), + mCloneReadInfo.mFiles); + return NS_OK; +} + +nsresult +DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "Must have a key range here!"); + + PROFILER_LABEL("DeleteHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCString keyRangeClause; + mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); + + NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); + + nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause; + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +DeleteHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + aVal.setUndefined(); + return NS_OK; +} + +nsresult +DeleteHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(mKeyRange, "This should never be null!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("DeleteHelper", "PackArgumentsForParentProcess", + js::ProfileEntry::Category::STORAGE); + + DeleteParams params; + + mKeyRange->ToSerializedKeyRange(params.keyRange()); + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +DeleteHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("DeleteHelper", "SendResponseToChildProcess", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + response = DeleteResponse(); + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +DeleteHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TDeleteResponse, + "Bad response type!"); + + return NS_OK; +} + +nsresult +ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aConnection, "Passed a null connection!"); + + PROFILER_LABEL("ClearHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement( + NS_LITERAL_CSTRING("DELETE FROM object_data " + "WHERE object_store_id = :osid")); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->Execute(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +ClearHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("ClearHelper", "PackArgumentsForParentProcess", + js::ProfileEntry::Category::STORAGE); + + aParams = ClearParams(); + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +ClearHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("ClearHelper", "SendResponseToChildProcess", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + response = ClearResponse(); + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +ClearHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TClearResponse, + "Bad response type!"); + + return NS_OK; +} + +nsresult +OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenCursorHelper", "DoDatabaseWork [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + + nsCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(keyValue, keyRangeClause); + } + + nsAutoCString directionClause; + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause.AssignLiteral(" ORDER BY key_value ASC"); + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + directionClause.AssignLiteral(" ORDER BY key_value DESC"); + break; + + default: + NS_NOTREACHED("Unknown direction type!"); + } + + nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids " + "FROM object_data " + "WHERE object_store_id = :id") + + keyRangeClause + directionClause + + NS_LITERAL_CSTRING(" LIMIT 1"); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement(firstQuery); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!hasResult) { + mKey.Unset(); + return NS_OK; + } + + rv = mKey.SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, + mDatabase, mCloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + // Now we need to make the query to get the next match. + keyRangeClause.Truncate(); + nsAutoCString continueToKeyRangeClause; + + NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + AppendConditionClause(keyValue, currentKey, false, false, + keyRangeClause); + AppendConditionClause(keyValue, currentKey, false, true, + continueToKeyRangeClause); + if (mKeyRange && !mKeyRange->Upper().IsUnset()) { + AppendConditionClause(keyValue, rangeKey, true, + !mKeyRange->IsUpperOpen(), keyRangeClause); + AppendConditionClause(keyValue, rangeKey, true, + !mKeyRange->IsUpperOpen(), + continueToKeyRangeClause); + mRangeKey = mKeyRange->Upper(); + } + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); + AppendConditionClause(keyValue, currentKey, true, true, + continueToKeyRangeClause); + if (mKeyRange && !mKeyRange->Lower().IsUnset()) { + AppendConditionClause(keyValue, rangeKey, false, + !mKeyRange->IsLowerOpen(), keyRangeClause); + AppendConditionClause(keyValue, rangeKey, false, + !mKeyRange->IsLowerOpen(), + continueToKeyRangeClause); + mRangeKey = mKeyRange->Lower(); + } + break; + + default: + NS_NOTREACHED("Unknown direction type!"); + } + + NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids " + "FROM object_data " + "WHERE object_store_id = :id"); + + mContinueQuery = queryStart + keyRangeClause + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + + mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + + NS_LITERAL_CSTRING(" LIMIT "); + + return NS_OK; +} + +nsresult +OpenCursorHelper::EnsureCursor() +{ + if (mCursor || mKey.IsUnset()) { + return NS_OK; + } + + mSerializedCloneReadInfo = mCloneReadInfo; + + NS_ASSERTION(mSerializedCloneReadInfo.data && + mSerializedCloneReadInfo.dataLength, + "Shouldn't be possible!"); + + nsRefPtr cursor = + IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection, + mRangeKey, mContinueQuery, mContinueToQuery, mKey, + Move(mCloneReadInfo)); + IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!"); + + mCursor.swap(cursor); + return NS_OK; +} + +nsresult +OpenCursorHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + nsresult rv = EnsureCursor(); + NS_ENSURE_SUCCESS(rv, rv); + + if (mCursor) { + rv = WrapNative(aCx, mCursor, aVal); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + aVal.setUndefined(); + } + + return NS_OK; +} + +void +OpenCursorHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); + + mCursor = nullptr; + + // These don't need to be released on the main thread but they're only valid + // as long as mCursor is set. + mSerializedCloneReadInfo.data = nullptr; + mSerializedCloneReadInfo.dataLength = 0; + + ObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +OpenCursorHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + OpenCursorParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.direction() = mDirection; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); + + PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + InfallibleTArray blobsParent; + + if (NS_SUCCEEDED(aResultCode)) { + IDBDatabase* database = mObjectStore->Transaction()->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + const nsTArray& files = mCloneReadInfo.mFiles; + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobsParent); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobsToActors failed!"); + } + } + + if (NS_SUCCEEDED(aResultCode)) { + nsresult rv = EnsureCursor(); + if (NS_FAILED(rv)) { + NS_WARNING("EnsureCursor failed!"); + aResultCode = rv; + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + OpenCursorResponse openCursorResponse; + + if (!mCursor) { + openCursorResponse = mozilla::void_t(); + } + else { + IndexedDBObjectStoreParent* objectStoreActor = + mObjectStore->GetActorParent(); + NS_ASSERTION(objectStoreActor, "Must have an actor here!"); + + IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); + NS_ASSERTION(requestActor, "Must have an actor here!"); + + NS_ASSERTION(mSerializedCloneReadInfo.data && + mSerializedCloneReadInfo.dataLength, + "Shouldn't be possible!"); + + ObjectStoreCursorConstructorParams params; + params.requestParent() = requestActor; + params.direction() = mDirection; + params.key() = mKey; + params.optionalCloneInfo() = mSerializedCloneReadInfo; + params.blobsParent().SwapElements(blobsParent); + + if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) { + return Error; + } + } + + response = openCursorResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +OpenCursorHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse, + "Bad response type!"); + NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() == + OpenCursorResponse::Tvoid_t || + aResponseValue.get_OpenCursorResponse().type() == + OpenCursorResponse::TPIndexedDBCursorChild, + "Bad response union type!"); + NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); + + const OpenCursorResponse& response = + aResponseValue.get_OpenCursorResponse(); + + switch (response.type()) { + case OpenCursorResponse::Tvoid_t: + break; + + case OpenCursorResponse::TPIndexedDBCursorChild: { + IndexedDBCursorChild* actor = + static_cast( + response.get_PIndexedDBCursorChild()); + + mCursor = actor->ForgetStrongCursor(); + NS_ASSERTION(mCursor, "This should never be null!"); + + } break; + + default: + MOZ_CRASH(); + } + + return NS_OK; +} + +nsresult +OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + PROFILER_LABEL("OpenKeyCursorHelper", "DoDatabaseWork [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); + + nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT ") + keyValue + + NS_LITERAL_CSTRING(" FROM object_data WHERE " + "object_store_id = :") + + id; + + nsAutoCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(keyValue, keyRangeClause); + } + + nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause.AppendLiteral(" ASC"); + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + directionClause.AppendLiteral(" DESC"); + break; + + default: + MOZ_CRASH("Unknown direction type!"); + } + + nsCString firstQuery = queryStart + keyRangeClause + directionClause + + openLimit + NS_LITERAL_CSTRING("1"); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement(firstQuery); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!hasResult) { + mKey.Unset(); + return NS_OK; + } + + rv = mKey.SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + + // Now we need to make the query to get the next match. + keyRangeClause.Truncate(); + nsAutoCString continueToKeyRangeClause; + + NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + AppendConditionClause(keyValue, currentKey, false, false, + keyRangeClause); + AppendConditionClause(keyValue, currentKey, false, true, + continueToKeyRangeClause); + if (mKeyRange && !mKeyRange->Upper().IsUnset()) { + AppendConditionClause(keyValue, rangeKey, true, + !mKeyRange->IsUpperOpen(), keyRangeClause); + AppendConditionClause(keyValue, rangeKey, true, + !mKeyRange->IsUpperOpen(), + continueToKeyRangeClause); + mRangeKey = mKeyRange->Upper(); + } + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); + AppendConditionClause(keyValue, currentKey, true, true, + continueToKeyRangeClause); + if (mKeyRange && !mKeyRange->Lower().IsUnset()) { + AppendConditionClause(keyValue, rangeKey, false, + !mKeyRange->IsLowerOpen(), keyRangeClause); + AppendConditionClause(keyValue, rangeKey, false, + !mKeyRange->IsLowerOpen(), + continueToKeyRangeClause); + mRangeKey = mKeyRange->Lower(); + } + break; + + default: + MOZ_CRASH("Unknown direction type!"); + } + + mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit; + mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + + openLimit; + + return NS_OK; +} + +nsresult +OpenKeyCursorHelper::EnsureCursor() +{ + MOZ_ASSERT(NS_IsMainThread()); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "EnsureCursor [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + if (mCursor || mKey.IsUnset()) { + return NS_OK; + } + + mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection, + mRangeKey, mContinueQuery, mContinueToQuery, + mKey); + IDB_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + MOZ_ASSERT(NS_IsMainThread()); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "GetSuccessResult [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = EnsureCursor(); + NS_ENSURE_SUCCESS(rv, rv); + + if (mCursor) { + rv = WrapNative(aCx, mCursor, aVal); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + aVal.setUndefined(); + } + + return NS_OK; +} + +void +OpenKeyCursorHelper::ReleaseMainThreadObjects() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mKeyRange = nullptr; + mCursor = nullptr; + + ObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +OpenKeyCursorHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + OpenKeyCursorParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.direction() = mDirection; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(!mCursor); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + MOZ_ASSERT(actor); + + if (NS_SUCCEEDED(aResultCode)) { + nsresult rv = EnsureCursor(); + if (NS_FAILED(rv)) { + NS_WARNING("EnsureCursor failed!"); + aResultCode = rv; + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } else { + OpenCursorResponse openCursorResponse; + + if (!mCursor) { + openCursorResponse = mozilla::void_t(); + } + else { + IndexedDBObjectStoreParent* objectStoreActor = + mObjectStore->GetActorParent(); + MOZ_ASSERT(objectStoreActor); + + IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); + MOZ_ASSERT(requestActor); + + ObjectStoreCursorConstructorParams params; + params.requestParent() = requestActor; + params.direction() = mDirection; + params.key() = mKey; + params.optionalCloneInfo() = mozilla::void_t(); + + if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) { + return Error; + } + } + + response = openCursorResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +OpenKeyCursorHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse); + MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() == + OpenCursorResponse::Tvoid_t || + aResponseValue.get_OpenCursorResponse().type() == + OpenCursorResponse::TPIndexedDBCursorChild); + MOZ_ASSERT(!mCursor); + + PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "UnpackResponseFromParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + const OpenCursorResponse& response = + aResponseValue.get_OpenCursorResponse(); + + switch (response.type()) { + case OpenCursorResponse::Tvoid_t: + break; + + case OpenCursorResponse::TPIndexedDBCursorChild: { + IndexedDBCursorChild* actor = + static_cast( + response.get_PIndexedDBCursorChild()); + + mCursor = actor->ForgetStrongCursor(); + NS_ASSERTION(mCursor, "This should never be null!"); + + } break; + + default: + MOZ_CRASH("Unknown response union type!"); + } + + return NS_OK; +} + +nsresult +CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("CreateIndexHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { + NS_WARNING("Refusing to create index because disk space is low!"); + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + + // Insert the data into the database. + nsCOMPtr stmt = + mTransaction->GetCachedStatement( + "INSERT INTO object_store_index (id, name, key_path, unique_index, " + "multientry, object_store_id) " + "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)" + ); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mIndex->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsAutoString keyPathSerialization; + mIndex->GetKeyPath().SerializeToString(keyPathSerialization); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), + keyPathSerialization); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"), + mIndex->IsUnique() ? 1 : 0); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"), + mIndex->IsMultiEntry() ? 1 : 0); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mIndex->ObjectStore()->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (NS_FAILED(stmt->Execute())) { + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + } + +#ifdef DEBUG + { + int64_t id; + aConnection->GetLastInsertRowID(&id); + NS_ASSERTION(mIndex->Id() == id, "Bad index id!"); + } +#endif + + // Now we need to populate the index with data from the object store. + rv = InsertDataFromObjectStore(aConnection); + if (NS_FAILED(rv)) { + return rv; + } + + return NS_OK; +} + +void +CreateIndexHelper::ReleaseMainThreadObjects() +{ + mIndex = nullptr; + NoRequestObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection) +{ + nsCOMPtr stmt = + mTransaction->GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM " + "object_data WHERE object_store_id = :osid")); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mIndex->ObjectStore()->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + IDB_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, + NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (!hasResult) { + // Bail early if we have no data to avoid creating the below runtime + return NS_OK; + } + + ThreadLocalJSRuntime* tlsEntry = + reinterpret_cast(PR_GetThreadPrivate(sTLSIndex)); + + if (!tlsEntry) { + tlsEntry = ThreadLocalJSRuntime::Create(); + IDB_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + PR_SetThreadPrivate(sTLSIndex, tlsEntry); + } + + JSContext* cx = tlsEntry->Context(); + JSAutoRequest ar(cx); + JSAutoCompartment ac(cx, tlsEntry->Global()); + + do { + StructuredCloneReadInfo cloneReadInfo; + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, + mDatabase, cloneReadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer; + + JSStructuredCloneCallbacks callbacks = { + IDBObjectStore::StructuredCloneReadCallback, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + + JS::Rooted clone(cx); + if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) { + NS_WARNING("Failed to deserialize structured clone data!"); + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + + nsTArray updateInfo; + rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(), + mIndex->GetKeyPath(), + mIndex->IsUnique(), + mIndex->IsMultiEntry(), + tlsEntry->Context(), + clone, updateInfo); + NS_ENSURE_SUCCESS(rv, rv); + + int64_t objectDataID = stmt->AsInt64(0); + + Key key; + rv = key.SetFromStatement(stmt, 3); + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(), + key, false, objectDataID, updateInfo); + NS_ENSURE_SUCCESS(rv, rv); + + } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +void +CreateIndexHelper::DestroyTLSEntry(void* aPtr) +{ + delete reinterpret_cast(aPtr); +} + +nsresult +DeleteIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("DeleteIndexHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr stmt = + mTransaction->GetCachedStatement( + "DELETE FROM object_store_index " + "WHERE name = :name " + ); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (NS_FAILED(stmt->Execute())) { + return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; + } + + return NS_OK; +} + +nsresult +GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("GetAllHelper", "DoDatabaseWork [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); + NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); + + nsAutoCString keyRangeClause; + if (mKeyRange) { + if (!mKeyRange->Lower().IsUnset()) { + keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); + if (mKeyRange->IsLowerOpen()) { + keyRangeClause.AppendLiteral(" > :"); + } + else { + keyRangeClause.AppendLiteral(" >= :"); + } + keyRangeClause.Append(lowerKeyName); + } + + if (!mKeyRange->Upper().IsUnset()) { + keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); + if (mKeyRange->IsUpperOpen()) { + keyRangeClause.AppendLiteral(" < :"); + } + else { + keyRangeClause.AppendLiteral(" <= :"); + } + keyRangeClause.Append(upperKeyName); + } + } + + nsAutoCString limitClause; + if (mLimit != UINT32_MAX) { + limitClause.AssignLiteral(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause + + NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + + limitClause; + + mCloneReadInfos.SetCapacity(50); + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + if (!mKeyRange->Lower().IsUnset()) { + rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + if (!mKeyRange->Upper().IsUnset()) { + rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + bool hasResult; + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) { + mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2); + } + + StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement(); + NS_ASSERTION(readInfo, "Shouldn't fail since SetCapacity succeeded!"); + + rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, + mDatabase, *readInfo); + NS_ENSURE_SUCCESS(rv, rv); + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +GetAllHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!"); + + nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal); + + NS_ASSERTION(mCloneReadInfos.IsEmpty(), + "Should have cleared in ConvertToArrayAndCleanup"); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +GetAllHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { + IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); + } + ObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +GetAllHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + GetAllParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.limit() = mLimit; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetAllHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + GetAllResponse getAllResponse; + if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) { + IDBDatabase* database = mObjectStore->Transaction()->Database(); + NS_ASSERTION(database, "This should never be null!"); + + nsIContentParent* contentParent = database->GetContentParent(); + NS_ASSERTION(contentParent, "This should never be null!"); + + FileManager* fileManager = database->Manager(); + NS_ASSERTION(fileManager, "This should never be null!"); + + uint32_t length = mCloneReadInfos.Length(); + + InfallibleTArray& infos = + getAllResponse.cloneInfos(); + infos.SetCapacity(length); + + InfallibleTArray& blobArrays = getAllResponse.blobs(); + blobArrays.SetCapacity(length); + + for (uint32_t index = 0; + NS_SUCCEEDED(aResultCode) && index < length; + index++) { + // Append the structured clone data. + const StructuredCloneReadInfo& clone = mCloneReadInfos[index]; + SerializedStructuredCloneReadInfo* info = infos.AppendElement(); + *info = clone; + + // Now take care of the files. + const nsTArray& files = clone.mFiles; + BlobArray* blobArray = blobArrays.AppendElement(); + InfallibleTArray& blobs = blobArray->blobsParent(); + + aResultCode = + IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, + blobs); + if (NS_FAILED(aResultCode)) { + NS_WARNING("ConvertBlobsToActors failed!"); + break; + } + } + } + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + response = getAllResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetAllHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse, + "Bad response type!"); + + const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse(); + const InfallibleTArray& cloneInfos = + getAllResponse.cloneInfos(); + const InfallibleTArray& blobArrays = getAllResponse.blobs(); + + mCloneReadInfos.SetCapacity(cloneInfos.Length()); + + for (uint32_t index = 0; index < cloneInfos.Length(); index++) { + const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index]; + const InfallibleTArray& blobs = blobArrays[index].blobsChild(); + + StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement(); + if (!destInfo->SetFromSerialized(srcInfo)) { + IDB_WARNING("Failed to copy clone buffer!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles); + } + + return NS_OK; +} + +nsresult +GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + PROFILER_LABEL("GetAllKeysHelper", "DoDatabaseWork [IDObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + + nsAutoCString keyRangeClause; + if (mKeyRange) { + mKeyRange->GetBindingClause(keyValue, keyRangeClause); + } + + nsAutoCString limitClause; + if (mLimit != UINT32_MAX) { + limitClause = NS_LITERAL_CSTRING(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + NS_NAMED_LITERAL_CSTRING(osid, "osid"); + + nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyValue + + NS_LITERAL_CSTRING(" FROM object_data WHERE " + "object_store_id = :") + + osid + keyRangeClause + + NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + + limitClause; + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + rv = mKeyRange->BindToStatement(stmt); + NS_ENSURE_SUCCESS(rv, rv); + } + + mKeys.SetCapacity(std::min(50, mLimit)); + + bool hasResult; + while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + if (mKeys.Capacity() == mKeys.Length()) { + mKeys.SetCapacity(mKeys.Capacity() * 2); + } + + Key* key = mKeys.AppendElement(); + NS_ASSERTION(key, "This shouldn't fail!"); + + rv = key->SetFromStatement(stmt, 0); + NS_ENSURE_SUCCESS(rv, rv); + } + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + return NS_OK; +} + +nsresult +GetAllKeysHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mKeys.Length() <= mLimit); + + PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "GetSuccessResult [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + nsTArray keys; + mKeys.SwapElements(keys); + + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (!array) { + IDB_WARNING("Failed to make array!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!keys.IsEmpty()) { + if (!JS_SetArrayLength(aCx, array, keys.Length())) { + IDB_WARNING("Failed to set array length!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0, count = keys.Length(); index < count; index++) { + const Key& key = keys[index]; + MOZ_ASSERT(!key.IsUnset()); + + JS::Rooted value(aCx); + nsresult rv = key.ToJSVal(aCx, &value); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get jsval for key!"); + return rv; + } + + if (!JS_SetElement(aCx, array, index, value)) { + IDB_WARNING("Failed to set array element!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aVal.setObject(*array); + return NS_OK; +} + +void +GetAllKeysHelper::ReleaseMainThreadObjects() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mKeyRange = nullptr; + + ObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +GetAllKeysHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + GetAllKeysParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } else { + params.optionalKeyRange() = mozilla::void_t(); + } + + params.limit() = mLimit; + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + MOZ_ASSERT(actor); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + GetAllKeysResponse getAllKeysResponse; + getAllKeysResponse.keys().AppendElements(mKeys); + response = getAllKeysResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +GetAllKeysHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse); + + mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys()); + return NS_OK; +} + +nsresult +CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("CountHelper", "DoDatabaseWork [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); + NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); + + nsAutoCString keyRangeClause; + if (mKeyRange) { + if (!mKeyRange->Lower().IsUnset()) { + keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); + if (mKeyRange->IsLowerOpen()) { + keyRangeClause.AppendLiteral(" > :"); + } + else { + keyRangeClause.AppendLiteral(" >= :"); + } + keyRangeClause.Append(lowerKeyName); + } + + if (!mKeyRange->Upper().IsUnset()) { + keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); + if (mKeyRange->IsUpperOpen()) { + keyRangeClause.AppendLiteral(" < :"); + } + else { + keyRangeClause.AppendLiteral(" <= :"); + } + keyRangeClause.Append(upperKeyName); + } + } + + nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause; + + nsCOMPtr stmt = mTransaction->GetCachedStatement(query); + IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mObjectStore->Id()); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mKeyRange) { + if (!mKeyRange->Lower().IsUnset()) { + rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + if (!mKeyRange->Upper().IsUnset()) { + rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mCount = stmt->AsInt64(0); + return NS_OK; +} + +nsresult +CountHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + aVal.setNumber(static_cast(mCount)); + return NS_OK; +} + +void +CountHelper::ReleaseMainThreadObjects() +{ + mKeyRange = nullptr; + ObjectStoreHelper::ReleaseMainThreadObjects(); +} + +nsresult +CountHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, + nsIContentChild* aBlobCreator) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + + PROFILER_MAIN_THREAD_LABEL("CountHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + CountParams params; + + if (mKeyRange) { + KeyRange keyRange; + mKeyRange->ToSerializedKeyRange(keyRange); + params.optionalKeyRange() = keyRange; + } + else { + params.optionalKeyRange() = mozilla::void_t(); + } + + aParams = params; + return NS_OK; +} + +AsyncConnectionHelper::ChildProcessSendResult +CountHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_MAIN_THREAD_LABEL("CountHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", + js::ProfileEntry::Category::STORAGE); + + IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); + NS_ASSERTION(actor, "How did we get this far without an actor?"); + + ResponseValue response; + if (NS_FAILED(aResultCode)) { + response = aResultCode; + } + else { + CountResponse countResponse = mCount; + response = countResponse; + } + + if (!actor->SendResponse(response)) { + return Error; + } + + return Success_Sent; +} + +nsresult +CountHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse, + "Bad response type!"); + + mCount = aResponseValue.get_CountResponse().count(); + return NS_OK; +} diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index 1ce9c869bac6..0d164c8ffaee 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -7,68 +7,61 @@ #ifndef mozilla_dom_indexeddb_idbobjectstore_h__ #define mozilla_dom_indexeddb_idbobjectstore_h__ -#include "js/RootingAPI.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + +#include "js/TypeDecls.h" #include "mozilla/dom/IDBCursorBinding.h" #include "mozilla/dom/IDBIndexBinding.h" -#include "nsAutoPtr.h" +#include "mozilla/dom/IDBObjectStoreBinding.h" #include "nsCycleCollectionParticipant.h" -#include "nsISupports.h" -#include "nsString.h" -#include "nsTArray.h" -#include "nsWrapperCache.h" +#include "MainThreadUtils.h" -struct JSClass; +#include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" +#include "mozilla/dom/indexedDB/KeyPath.h" + +class nsIDOMBlob; +class nsIScriptContext; class nsPIDOMWindow; namespace mozilla { - -class ErrorResult; - namespace dom { - -class DOMStringList; class nsIContentParent; -template class Sequence; +class PBlobChild; +class PBlobParent; +} +} -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE +class AsyncConnectionHelper; class FileManager; +class IDBCursor; class IDBKeyRange; class IDBRequest; -class IDBTransaction; -class IndexUpdateInfo; +class IndexedDBObjectStoreChild; +class IndexedDBObjectStoreParent; class Key; -class KeyPath; -class ObjectStoreSpec; -struct StructuredCloneFile; -struct StructuredCloneReadInfo; -class IDBObjectStore MOZ_FINAL - : public nsISupports - , public nsWrapperCache +struct IndexInfo; +struct IndexUpdateInfo; +struct ObjectStoreInfo; + +struct MutableFileData; +struct BlobOrFileData; + +class IDBObjectStore MOZ_FINAL : public nsISupports, + public nsWrapperCache { - static const JSClass sDummyPropJSClass; - - nsRefPtr mTransaction; - JS::Heap mCachedKeyPath; - - // This normally points to the ObjectStoreSpec owned by the parent IDBDatabase - // object. However, if this objectStore is part of a versionchange transaction - // and it gets deleted then the spec is copied into mDeletedSpec and mSpec is - // set to point at mDeletedSpec. - const ObjectStoreSpec* mSpec; - nsAutoPtr mDeletedSpec; - - nsTArray> mIndexes; - - const int64_t mId; - bool mRooted; - public: - struct StructuredCloneWriteInfo; + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore) static already_AddRefed - Create(IDBTransaction* aTransaction, const ObjectStoreSpec& aSpec); + Create(IDBTransaction* aTransaction, + ObjectStoreInfo* aInfo, + const nsACString& aDatabaseId, + bool aCreating); static nsresult AppendIndexUpdateInfo(int64_t aIndexID, @@ -79,62 +72,214 @@ public: JS::Handle aObject, nsTArray& aUpdateInfoArray); + static nsresult + UpdateIndexes(IDBTransaction* aTransaction, + int64_t aObjectStoreId, + const Key& aObjectStoreKey, + bool aOverwrite, + int64_t aObjectDataId, + const nsTArray& aUpdateInfoArray); + + static nsresult + GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement, + uint32_t aDataIndex, + uint32_t aFileIdsIndex, + IDBDatabase* aDatabase, + StructuredCloneReadInfo& aInfo); + static void ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo); + static void + ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo); + static bool DeserializeValue(JSContext* aCx, StructuredCloneReadInfo& aCloneReadInfo, JS::MutableHandle aValue); static bool - DeserializeIndexValue(JSContext* aCx, - StructuredCloneReadInfo& aCloneReadInfo, - JS::MutableHandle aValue); + SerializeValue(JSContext* aCx, + StructuredCloneWriteInfo& aCloneWriteInfo, + JS::Handle aValue); - static const JSClass* - DummyPropClass() + template + static JSObject* + StructuredCloneReadCallback(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + uint32_t aData, + void* aClosure); + static bool + StructuredCloneWriteCallback(JSContext* aCx, + JSStructuredCloneWriter* aWriter, + JS::Handle aObj, + void* aClosure); + + static nsresult + ConvertFileIdsToArray(const nsAString& aFileIds, + nsTArray& aResult); + + // Called only in the main process. + static nsresult + ConvertBlobsToActors(nsIContentParent* aContentParent, + FileManager* aFileManager, + const nsTArray& aFiles, + InfallibleTArray& aActors); + + // Called only in the child process. + static void + ConvertActorsToBlobs(const InfallibleTArray& aActors, + nsTArray& aFiles); + + const nsString& Name() const { - return &sDummyPropJSClass; + return mName; } - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - int64_t - Id() const + bool IsAutoIncrement() const { - AssertIsOnOwningThread(); + return mAutoIncrement; + } + bool IsWriteAllowed() const + { + return mTransaction->IsWriteAllowed(); + } + + int64_t Id() const + { + NS_ASSERTION(mId != INT64_MIN, "Don't ask for this yet!"); return mId; } - const nsString& - Name() const; + const KeyPath& GetKeyPath() const + { + return mKeyPath; + } - bool - AutoIncrement() const; + const bool HasValidKeyPath() const + { + return mKeyPath.IsValid(); + } - const KeyPath& - GetKeyPath() const; + IDBTransaction* Transaction() + { + return mTransaction; + } - bool - HasValidKeyPath() const; + ObjectStoreInfo* Info() + { + return mInfo; + } - nsPIDOMWindow* - GetParentObject() const; + void + SetActor(IndexedDBObjectStoreChild* aActorChild) + { + NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); + mActorChild = aActorChild; + } + + void + SetActor(IndexedDBObjectStoreParent* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, + "Shouldn't have more than one!"); + mActorParent = aActorParent; + } + + IndexedDBObjectStoreChild* + GetActorChild() const + { + return mActorChild; + } + + IndexedDBObjectStoreParent* + GetActorParent() const + { + return mActorParent; + } + + already_AddRefed + CreateIndexInternal(const IndexInfo& aInfo, + ErrorResult& aRv); + + nsresult AddOrPutInternal( + const SerializedStructuredCloneWriteInfo& aCloneWriteInfo, + const Key& aKey, + const InfallibleTArray& aUpdateInfoArray, + const nsTArray >& aBlobs, + bool aOverwrite, + IDBRequest** _retval); + + already_AddRefed + GetInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv); + + already_AddRefed + GetAllInternal(IDBKeyRange* aKeyRange, + uint32_t aLimit, + ErrorResult& aRv); + + already_AddRefed + GetAllKeysInternal(IDBKeyRange* aKeyRange, + uint32_t aLimit, + ErrorResult& aRv); + + already_AddRefed + DeleteInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv); + + already_AddRefed + CountInternal(IDBKeyRange* aKeyRange, + ErrorResult& aRv); + + already_AddRefed + OpenCursorInternal(IDBKeyRange* aKeyRange, + size_t aDirection, + ErrorResult& aRv); + + already_AddRefed + OpenKeyCursorInternal(IDBKeyRange* aKeyRange, + size_t aDirection, + ErrorResult& aRv); + + nsresult + OpenCursorFromChildProcess( + IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + const SerializedStructuredCloneReadInfo& aCloneInfo, + nsTArray& aBlobs, + IDBCursor** _retval); + + nsresult + OpenCursorFromChildProcess(IDBRequest* aRequest, + size_t aDirection, + const Key& aKey, + IDBCursor** _retval); + + void + SetInfo(ObjectStoreInfo* aInfo); + + static const JSClass sDummyPropJSClass; + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // WebIDL + IDBTransaction* + GetParentObject() const + { + return mTransaction; + } void GetName(nsString& aName) const { - AssertIsOnOwningThread(); - - aName = Name(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + aName.Assign(mName); } void @@ -142,38 +287,38 @@ public: ErrorResult& aRv); already_AddRefed - IndexNames(); + GetIndexNames(ErrorResult& aRv); IDBTransaction* Transaction() const { - AssertIsOnOwningThread(); - + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); return mTransaction; } - already_AddRefed - Add(JSContext* aCx, - JS::Handle aValue, - JS::Handle aKey, - ErrorResult& aRv) + bool + AutoIncrement() const { - AssertIsOnOwningThread(); - - return AddOrPut(aCx, aValue, aKey, false, aRv); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mAutoIncrement; } already_AddRefed - Put(JSContext* aCx, - JS::Handle aValue, - JS::Handle aKey, - ErrorResult& aRv) + Put(JSContext* aCx, JS::Handle aValue, + JS::Handle aKey, ErrorResult& aRv) { - AssertIsOnOwningThread(); - + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); return AddOrPut(aCx, aValue, aKey, true, aRv); } + already_AddRefed + Add(JSContext* aCx, JS::Handle aValue, + JS::Handle aKey, ErrorResult& aRv) + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return AddOrPut(aCx, aValue, aKey, false, aRv); + } + already_AddRefed Delete(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); @@ -183,19 +328,18 @@ public: already_AddRefed Clear(ErrorResult& aRv); - already_AddRefed - CreateIndex(JSContext* aCx, - const nsAString& aName, - const nsAString& aKeyPath, - const IDBIndexParameters& aOptionalParameters, - ErrorResult& aRv); + already_AddRefed + OpenCursor(JSContext* aCx, JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv); already_AddRefed - CreateIndex(JSContext* aCx, - const nsAString& aName, + CreateIndex(JSContext* aCx, const nsAString& aName, const nsAString& aKeyPath, + const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv); + + already_AddRefed + CreateIndex(JSContext* aCx, const nsAString& aName, const Sequence& aKeyPath, - const IDBIndexParameters& aOptionalParameters, - ErrorResult& aRv); + const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv); already_AddRefed Index(const nsAString& aName, ErrorResult &aRv); @@ -204,116 +348,70 @@ public: DeleteIndex(const nsAString& aIndexName, ErrorResult& aRv); already_AddRefed - Count(JSContext* aCx, - JS::Handle aKey, + Count(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); already_AddRefed - GetAll(JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, - ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv); - } + GetAll(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv); already_AddRefed - GetAllKeys(JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, - ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv); - } + GetAllKeys(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv); already_AddRefed - OpenCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection, - aRv); - } - - already_AddRefed - OpenKeyCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv) - { - AssertIsOnOwningThread(); - - return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, - aRv); - } - - void - RefreshSpec(bool aMayDelete); - - const ObjectStoreSpec& - Spec() const; - - void - NoteDeletion(); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore) - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - -private: - IDBObjectStore(IDBTransaction* aTransaction, const ObjectStoreSpec* aSpec); + OpenKeyCursor(JSContext* aCx, JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv); +protected: + IDBObjectStore(); ~IDBObjectStore(); - nsresult - GetAddInfo(JSContext* aCx, - JS::Handle aValue, - JS::Handle aKeyVal, - StructuredCloneWriteInfo& aCloneWriteInfo, - Key& aKey, - nsTArray& aUpdateInfoArray); + nsresult GetAddInfo(JSContext* aCx, + JS::Handle aValue, + JS::Handle aKeyVal, + StructuredCloneWriteInfo& aCloneWriteInfo, + Key& aKey, + nsTArray& aUpdateInfoArray); already_AddRefed - AddOrPut(JSContext* aCx, - JS::Handle aValue, - JS::Handle aKey, - bool aOverwrite, + AddOrPut(JSContext* aCx, JS::Handle aValue, + JS::Handle aKey, bool aOverwrite, ErrorResult& aRv); - already_AddRefed - GetAllInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, - ErrorResult& aRv); - already_AddRefed - CreateIndexInternal(JSContext* aCx, - const nsAString& aName, - const KeyPath& aKeyPath, - const IDBIndexParameters& aOptionalParameters, - ErrorResult& aRv); + CreateIndex(JSContext* aCx, const nsAString& aName, KeyPath& aKeyPath, + const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv); - already_AddRefed - OpenCursorInternal(bool aKeysOnly, - JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, - ErrorResult& aRv); + static void + ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer); + + static bool + ReadMutableFile(JSStructuredCloneReader* aReader, + MutableFileData* aRetval); + + static bool + ReadBlobOrFile(JSStructuredCloneReader* aReader, + uint32_t aTag, + BlobOrFileData* aRetval); +private: + nsRefPtr mTransaction; + + int64_t mId; + nsString mName; + KeyPath mKeyPath; + JS::Heap mCachedKeyPath; + bool mRooted; + bool mAutoIncrement; + nsCString mDatabaseId; + nsRefPtr mInfo; + + nsTArray > mCreatedIndexes; + + IndexedDBObjectStoreChild* mActorChild; + IndexedDBObjectStoreParent* mActorParent; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbobjectstore_h__ diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index 265257918859..e778a4bfdeec 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -6,88 +6,78 @@ #include "IDBRequest.h" -#include "BackgroundChildImpl.h" -#include "IDBCursor.h" -#include "IDBDatabase.h" -#include "IDBEvents.h" -#include "IDBFactory.h" -#include "IDBIndex.h" -#include "IDBObjectStore.h" -#include "IDBTransaction.h" +#include "nsIScriptContext.h" + #include "mozilla/ContentEvents.h" -#include "mozilla/ErrorResult.h" #include "mozilla/EventDispatcher.h" #include "mozilla/dom/ErrorEventBinding.h" #include "mozilla/dom/IDBOpenDBRequestBinding.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/UnionTypes.h" -#include "nsCOMPtr.h" +#include "nsComponentManagerUtils.h" +#include "nsDOMClassInfoID.h" +#include "nsDOMJSUtils.h" #include "nsContentUtils.h" -#include "nsIScriptContext.h" #include "nsJSUtils.h" #include "nsPIDOMWindow.h" #include "nsString.h" +#include "nsThreadUtils.h" +#include "nsWrapperCacheInlines.h" + +#include "AsyncConnectionHelper.h" +#include "IDBCursor.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IDBIndex.h" +#include "IDBObjectStore.h" +#include "IDBTransaction.h" #include "ReportInternalError.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +namespace { -using namespace mozilla::ipc; +#ifdef MOZ_ENABLE_PROFILER_SPS +uint64_t gNextRequestSerialNumber = 1; +#endif + +} // anonymous namespace + +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::OwningIDBObjectStoreOrIDBIndexOrIDBCursor; +using mozilla::dom::ErrorEventInit; +using namespace mozilla; IDBRequest::IDBRequest(IDBDatabase* aDatabase) - : IDBWrapperCache(aDatabase) +: IDBWrapperCache(aDatabase), + mResultVal(JSVAL_VOID), + mActorParent(nullptr), +#ifdef MOZ_ENABLE_PROFILER_SPS + mSerialNumber(gNextRequestSerialNumber++), +#endif + mErrorCode(NS_OK), + mLineNo(0), + mHaveResultOrErrorCode(false) { - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); - - InitMembers(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } IDBRequest::IDBRequest(nsPIDOMWindow* aOwner) - : IDBWrapperCache(aOwner) +: IDBWrapperCache(aOwner), + mResultVal(JSVAL_VOID), + mActorParent(nullptr), +#ifdef MOZ_ENABLE_PROFILER_SPS + mSerialNumber(gNextRequestSerialNumber++), +#endif + mErrorCode(NS_OK), + mLineNo(0), + mHaveResultOrErrorCode(false) { - InitMembers(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } IDBRequest::~IDBRequest() { - AssertIsOnOwningThread(); -} - -#ifdef DEBUG - -void -IDBRequest::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mOwningThread); - MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); -} - -#endif // DEBUG - -void -IDBRequest::InitMembers() -{ -#ifdef DEBUG - mOwningThread = PR_GetCurrentThread(); -#endif - AssertIsOnOwningThread(); - - mResultVal.setUndefined(); - mErrorCode = NS_OK; - mLineNo = 0; - mHaveResultOrErrorCode = false; - -#ifdef MOZ_ENABLE_PROFILER_SPS - { - BackgroundChildImpl::ThreadLocal* threadLocal = - BackgroundChildImpl::GetThreadLocalForCurrentThread(); - MOZ_ASSERT(threadLocal); - - mSerialNumber = threadLocal->mNextRequestSerialNumber++; - } -#endif + mResultVal = JSVAL_VOID; + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } // static @@ -95,15 +85,16 @@ already_AddRefed IDBRequest::Create(IDBDatabase* aDatabase, IDBTransaction* aTransaction) { - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); - - nsRefPtr request = new IDBRequest(aDatabase); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + nsRefPtr request(new IDBRequest(aDatabase)); request->mTransaction = aTransaction; request->SetScriptOwner(aDatabase->GetScriptOwner()); - request->CaptureCaller(); + if (!aDatabase->Factory()->FromIPC()) { + request->CaptureCaller(); + } + return request.forget(); } @@ -114,9 +105,6 @@ IDBRequest::Create(IDBObjectStore* aSourceAsObjectStore, IDBDatabase* aDatabase, IDBTransaction* aTransaction) { - MOZ_ASSERT(aSourceAsObjectStore); - aSourceAsObjectStore->AssertIsOnOwningThread(); - nsRefPtr request = Create(aDatabase, aTransaction); request->mSourceAsObjectStore = aSourceAsObjectStore; @@ -130,9 +118,6 @@ IDBRequest::Create(IDBIndex* aSourceAsIndex, IDBDatabase* aDatabase, IDBTransaction* aTransaction) { - MOZ_ASSERT(aSourceAsIndex); - aSourceAsIndex->AssertIsOnOwningThread(); - nsRefPtr request = Create(aDatabase, aTransaction); request->mSourceAsIndex = aSourceAsIndex; @@ -140,24 +125,31 @@ IDBRequest::Create(IDBIndex* aSourceAsIndex, return request.forget(); } +#ifdef DEBUG void -IDBRequest::GetSource( - Nullable& aSource) const +IDBRequest::AssertSourceIsCorrect() const { - AssertIsOnOwningThread(); + // At most one of mSourceAs* is allowed to be non-null. Check that by + // summing the double negation of each one and asserting the sum is at most + // 1. - MOZ_ASSERT_IF(mSourceAsObjectStore, !mSourceAsIndex); - MOZ_ASSERT_IF(mSourceAsIndex, !mSourceAsObjectStore); - MOZ_ASSERT_IF(mSourceAsCursor, mSourceAsObjectStore || mSourceAsIndex); + MOZ_ASSERT(!!mSourceAsObjectStore + !!mSourceAsIndex + !!mSourceAsCursor <= 1); +} +#endif - // Always check cursor first since cursor requests hold both the cursor and - // the objectStore or index the cursor came from. - if (mSourceAsCursor) { - aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor; - } else if (mSourceAsObjectStore) { +void +IDBRequest::GetSource(Nullable& aSource) const +{ + MOZ_ASSERT(NS_IsMainThread()); + + AssertSourceIsCorrect(); + + if (mSourceAsObjectStore) { aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore; } else if (mSourceAsIndex) { aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex; + } else if (mSourceAsCursor) { + aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor; } else { aSource.SetNull(); } @@ -166,63 +158,116 @@ IDBRequest::GetSource( void IDBRequest::Reset() { - AssertIsOnOwningThread(); - - mResultVal.setUndefined(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + mResultVal = JSVAL_VOID; mHaveResultOrErrorCode = false; mError = nullptr; } -void -IDBRequest::DispatchNonTransactionError(nsresult aErrorCode) +nsresult +IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) { - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aErrorCode)); - MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); + NS_ASSERTION(mResultVal.isUndefined(), "Should be undefined!"); - SetError(aErrorCode); + mHaveResultOrErrorCode = true; - // Make an error event and fire it at the target. - nsCOMPtr event = - CreateGenericEvent(this, - nsDependentString(kErrorEventType), - eDoesBubble, - eCancelable); - if (NS_WARN_IF(!event)) { + nsresult rv = aHelper->GetResultCode(); + + // If the request failed then set the error code and return. + if (NS_FAILED(rv)) { + SetError(rv); + return NS_OK; + } + + // See if our window is still valid. If not then we're going to pretend that + // we never completed. + if (NS_FAILED(CheckInnerWindowCorrectness())) { + return NS_OK; + } + + // Otherwise we need to get the result from the helper. + AutoJSAPI jsapi; + Maybe ac; + if (GetScriptOwner()) { + // If we have a script owner we want the SafeJSContext and then to enter + // the script owner's compartment. + jsapi.Init(); + ac.emplace(jsapi.cx(), GetScriptOwner()); + } else { + // Otherwise our owner is a window and we use that to initialize. + if (!jsapi.InitWithLegacyErrorReporting(GetOwner())) { + IDB_WARNING("Failed to initialise AutoJSAPI!"); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + SetError(rv); + return rv; + } + } + JSContext* cx = jsapi.cx(); + + AssertIsRooted(); + + JS::Rooted value(cx); + rv = aHelper->GetSuccessResult(cx, &value); + if (NS_FAILED(rv)) { + NS_WARNING("GetSuccessResult failed!"); + } + + if (NS_SUCCEEDED(rv)) { + mError = nullptr; + mResultVal = value; + } + else { + SetError(rv); + mResultVal = JSVAL_VOID; + } + + return rv; +} + +void +IDBRequest::NotifyHelperSentResultsToChildProcess(nsresult aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); + NS_ASSERTION(mResultVal.isUndefined(), "Should be undefined!"); + + // See if our window is still valid. If not then we're going to pretend that + // we never completed. + if (NS_FAILED(CheckInnerWindowCorrectness())) { return; } - bool ignored; - NS_WARN_IF(NS_FAILED(DispatchEvent(event, &ignored))); + mHaveResultOrErrorCode = true; + + if (NS_FAILED(aRv)) { + SetError(aRv); + } } void IDBRequest::SetError(nsresult aRv) { - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aRv)); - MOZ_ASSERT(NS_ERROR_GET_MODULE(aRv) == NS_ERROR_MODULE_DOM_INDEXEDDB); - MOZ_ASSERT(!mError); + NS_ASSERTION(NS_FAILED(aRv), "Er, what?"); + NS_ASSERTION(!mError, "Already have an error?"); mHaveResultOrErrorCode = true; - mError = new DOMError(GetOwner(), aRv); + mError = new mozilla::dom::DOMError(GetOwner(), aRv); mErrorCode = aRv; - mResultVal.setUndefined(); + mResultVal = JSVAL_VOID; } #ifdef DEBUG - nsresult IDBRequest::GetErrorCode() const { - AssertIsOnOwningThread(); - MOZ_ASSERT(mHaveResultOrErrorCode); - + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mHaveResultOrErrorCode, "Don't call me yet!"); return mErrorCode; } - -#endif // DEBUG +#endif void IDBRequest::CaptureCaller() @@ -232,6 +277,7 @@ IDBRequest::CaptureCaller() const char* filename = nullptr; uint32_t lineNo = 0; if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) { + NS_WARNING("Failed to get caller."); return; } @@ -246,25 +292,16 @@ IDBRequest::FillScriptErrorEvent(ErrorEventInit& aEventInit) const aEventInit.mFilename = mFilename; } -IDBRequestReadyState +mozilla::dom::IDBRequestReadyState IDBRequest::ReadyState() const { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return IsPending() ? - IDBRequestReadyState::Pending : - IDBRequestReadyState::Done; -} + if (IsPending()) { + return IDBRequestReadyState::Pending; + } -void -IDBRequest::SetSource(IDBCursor* aSource) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aSource); - MOZ_ASSERT(mSourceAsObjectStore || mSourceAsIndex); - MOZ_ASSERT(!mSourceAsCursor); - - mSourceAsCursor = aSource; + return IDBRequestReadyState::Done; } JSObject* @@ -277,72 +314,21 @@ void IDBRequest::GetResult(JS::MutableHandle aResult, ErrorResult& aRv) const { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mHaveResultOrErrorCode) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return; + // XXX Need a real error code here. + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); } JS::ExposeValueToActiveJS(mResultVal); aResult.set(mResultVal); } -void -IDBRequest::SetResultCallback(ResultCallback* aCallback) +mozilla::dom::DOMError* +IDBRequest::GetError(mozilla::ErrorResult& aRv) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aCallback); - MOZ_ASSERT(!mHaveResultOrErrorCode); - MOZ_ASSERT(mResultVal.isUndefined()); - MOZ_ASSERT(!mError); - - // See if our window is still valid. - if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) { - IDB_REPORT_INTERNAL_ERR(); - SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return; - } - - AutoJSAPI autoJS; - Maybe ac; - - if (GetScriptOwner()) { - // If we have a script owner we want the SafeJSContext and then to enter the - // script owner's compartment. - autoJS.Init(); - ac.emplace(autoJS.cx(), GetScriptOwner()); - } else { - // Otherwise our owner is a window and we use that to initialize. - MOZ_ASSERT(GetOwner()); - if (!autoJS.InitWithLegacyErrorReporting(GetOwner())) { - IDB_WARNING("Failed to initialize AutoJSAPI!"); - SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return; - } - } - - JSContext* cx = autoJS.cx(); - - AssertIsRooted(); - - JS::Rooted result(cx); - nsresult rv = aCallback->GetResult(cx, &result); - if (NS_WARN_IF(NS_FAILED(rv))) { - SetError(rv); - mResultVal.setUndefined(); - } else { - mError = nullptr; - mResultVal = result; - } - - mHaveResultOrErrorCode = true; -} - -DOMError* -IDBRequest::GetError(ErrorResult& aRv) -{ - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); if (!mHaveResultOrErrorCode) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); @@ -365,7 +351,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) - tmp->mResultVal.setUndefined(); + tmp->mResultVal = JSVAL_VOID; NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor) @@ -388,60 +374,41 @@ NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) nsresult IDBRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); aVisitor.mCanHandle = true; aVisitor.mParentTarget = mTransaction; return NS_OK; } -IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner) +IDBOpenDBRequest::IDBOpenDBRequest(nsPIDOMWindow* aOwner) : IDBRequest(aOwner) - , mFactory(aFactory) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aFactory); - - // aOwner may be null. + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } IDBOpenDBRequest::~IDBOpenDBRequest() { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } // static already_AddRefed -IDBOpenDBRequest::CreateForWindow(IDBFactory* aFactory, - nsPIDOMWindow* aOwner, - JS::Handle aScriptOwner) +IDBOpenDBRequest::Create(IDBFactory* aFactory, + nsPIDOMWindow* aOwner, + JS::Handle aScriptOwner) { - MOZ_ASSERT(aFactory); - aFactory->AssertIsOnOwningThread(); - MOZ_ASSERT(aOwner); - MOZ_ASSERT(aScriptOwner); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aFactory, "Null pointer!"); - nsRefPtr request = new IDBOpenDBRequest(aFactory, aOwner); - request->CaptureCaller(); + nsRefPtr request = new IDBOpenDBRequest(aOwner); request->SetScriptOwner(aScriptOwner); + request->mFactory = aFactory; - return request.forget(); -} - -// static -already_AddRefed -IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory, - JS::Handle aScriptOwner) -{ - MOZ_ASSERT(aFactory); - aFactory->AssertIsOnOwningThread(); - MOZ_ASSERT(aScriptOwner); - - nsRefPtr request = new IDBOpenDBRequest(aFactory, nullptr); - request->CaptureCaller(); - - request->SetScriptOwner(aScriptOwner); + if (!aFactory->FromIPC()) { + request->CaptureCaller(); + } return request.forget(); } @@ -449,9 +416,10 @@ IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory, void IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - MOZ_ASSERT(!aTransaction || !mTransaction); + NS_ASSERTION(!aTransaction || !mTransaction, + "Shouldn't have a transaction here!"); mTransaction = aTransaction; } @@ -477,20 +445,11 @@ NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) nsresult IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor) { - // XXX Fix me! - MOZ_ASSERT(NS_IsMainThread()); - return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); } JSObject* IDBOpenDBRequest::WrapObject(JSContext* aCx) { - AssertIsOnOwningThread(); - return IDBOpenDBRequestBinding::Wrap(aCx, this); } - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index eda2d4e41cf9..d628b5a7998f 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -7,98 +7,72 @@ #ifndef mozilla_dom_indexeddb_idbrequest_h__ #define mozilla_dom_indexeddb_idbrequest_h__ -#include "js/RootingAPI.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" +#include "mozilla/dom/DOMError.h" #include "mozilla/dom/IDBRequestBinding.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" -#include "nsAutoPtr.h" +#include "mozilla/ErrorResult.h" #include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" + +class nsIScriptContext; class nsPIDOMWindow; -struct PRThread; namespace mozilla { - -class ErrorResult; - +class EventChainPostVisitor; +class EventChainPreVisitor; namespace dom { - -class DOMError; -struct ErrorEventInit; -template struct Nullable; class OwningIDBObjectStoreOrIDBIndexOrIDBCursor; +struct ErrorEventInit; +} +} -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE +class HelperBase; class IDBCursor; -class IDBDatabase; class IDBFactory; class IDBIndex; class IDBObjectStore; class IDBTransaction; +class IndexedDBRequestParentBase; -class IDBRequest - : public IDBWrapperCache +class IDBRequest : public IDBWrapperCache { -protected: - // mSourceAsObjectStore and mSourceAsIndex are exclusive and one must always - // be set. mSourceAsCursor is sometimes set also. - nsRefPtr mSourceAsObjectStore; - nsRefPtr mSourceAsIndex; - nsRefPtr mSourceAsCursor; - - nsRefPtr mTransaction; - -#ifdef DEBUG - PRThread* mOwningThread; -#endif - - JS::Heap mResultVal; - nsRefPtr mError; - - nsString mFilename; -#ifdef MOZ_ENABLE_PROFILER_SPS - uint64_t mSerialNumber; -#endif - nsresult mErrorCode; - uint32_t mLineNo; - bool mHaveResultOrErrorCode; - public: - class ResultCallback; + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest, + IDBWrapperCache) - static already_AddRefed - Create(IDBDatabase* aDatabase, IDBTransaction* aTransaction); + static + already_AddRefed Create(IDBDatabase* aDatabase, + IDBTransaction* aTransaction); - static already_AddRefed - Create(IDBObjectStore* aSource, - IDBDatabase* aDatabase, - IDBTransaction* aTransaction); + static + already_AddRefed Create(IDBObjectStore* aSource, + IDBDatabase* aDatabase, + IDBTransaction* aTransaction); - static already_AddRefed - Create(IDBIndex* aSource, - IDBDatabase* aDatabase, - IDBTransaction* aTransaction); + static + already_AddRefed Create(IDBIndex* aSource, + IDBDatabase* aDatabase, + IDBTransaction* aTransaction); // nsIDOMEventTarget - virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; + virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; - void - GetSource(Nullable& aSource) const; + void GetSource(Nullable& aSource) const; - void - Reset(); + void Reset(); - void - DispatchNonTransactionError(nsresult aErrorCode); + nsresult NotifyHelperCompleted(HelperBase* aHelper); + void NotifyHelperSentResultsToChildProcess(nsresult aRv); - void - SetResultCallback(ResultCallback* aCallback); - - void - SetError(nsresult aRv); + void SetError(nsresult aRv); nsresult GetErrorCode() const @@ -110,11 +84,25 @@ public: } #endif - DOMError* - GetError(ErrorResult& aRv); + DOMError* GetError(ErrorResult& aRv); void - FillScriptErrorEvent(ErrorEventInit& aEventInit) const; + SetActor(IndexedDBRequestParentBase* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, + "Shouldn't have more than one!"); + mActorParent = aActorParent; + } + + IndexedDBRequestParentBase* + GetActorParent() const + { + return mActorParent; + } + + void CaptureCaller(); + + void FillScriptErrorEvent(ErrorEventInit& aEventInit) const; bool IsPending() const @@ -130,6 +118,11 @@ public: } #endif + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // WebIDL nsPIDOMWindow* GetParentObject() const { @@ -149,87 +142,66 @@ public: IDBTransaction* GetTransaction() const { - AssertIsOnOwningThread(); - + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); return mTransaction; } IDBRequestReadyState ReadyState() const; - void - SetSource(IDBCursor* aSource); - IMPL_EVENT_HANDLER(success); IMPL_EVENT_HANDLER(error); - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest, - IDBWrapperCache) - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - protected: explicit IDBRequest(IDBDatabase* aDatabase); explicit IDBRequest(nsPIDOMWindow* aOwner); ~IDBRequest(); - void - InitMembers(); + // At most one of these three fields can be non-null. + nsRefPtr mSourceAsObjectStore; + nsRefPtr mSourceAsIndex; + nsRefPtr mSourceAsCursor; - void - ConstructResult(); + // Check that the above condition holds. +#ifdef DEBUG + void AssertSourceIsCorrect() const; +#else + void AssertSourceIsCorrect() const {} +#endif - void - CaptureCaller(); + nsRefPtr mTransaction; + + JS::Heap mResultVal; + nsRefPtr mError; + IndexedDBRequestParentBase* mActorParent; + nsString mFilename; +#ifdef MOZ_ENABLE_PROFILER_SPS + uint64_t mSerialNumber; +#endif + nsresult mErrorCode; + uint32_t mLineNo; + bool mHaveResultOrErrorCode; }; -class NS_NO_VTABLE IDBRequest::ResultCallback +class IDBOpenDBRequest : public IDBRequest { public: - virtual nsresult - GetResult(JSContext* aCx, JS::MutableHandle aResult) = 0; + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) -protected: - ResultCallback() - { } -}; + static + already_AddRefed + Create(IDBFactory* aFactory, + nsPIDOMWindow* aOwner, + JS::Handle aScriptOwner); -class IDBOpenDBRequest MOZ_FINAL - : public IDBRequest -{ - // Only touched on the owning thread. - nsRefPtr mFactory; - -public: - static already_AddRefed - CreateForWindow(IDBFactory* aFactory, - nsPIDOMWindow* aOwner, - JS::Handle aScriptOwner); - - static already_AddRefed - CreateForJS(IDBFactory* aFactory, - JS::Handle aScriptOwner); - - void - SetTransaction(IDBTransaction* aTransaction); + void SetTransaction(IDBTransaction* aTransaction); // nsIDOMEventTarget - virtual nsresult - PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; + virtual nsresult PostHandleEvent( + EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; - DOMError* - GetError(ErrorResult& aRv) + DOMError* GetError(ErrorResult& aRv) { return IDBRequest::GetError(aRv); } @@ -240,24 +212,22 @@ public: return mFactory; } - IMPL_EVENT_HANDLER(blocked); - IMPL_EVENT_HANDLER(upgradeneeded); - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) - // nsWrapperCache virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; -private: - IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner); + // WebIDL + IMPL_EVENT_HANDLER(blocked); + IMPL_EVENT_HANDLER(upgradeneeded); +protected: + explicit IDBOpenDBRequest(nsPIDOMWindow* aOwner); ~IDBOpenDBRequest(); + + // Only touched on the main thread. + nsRefPtr mFactory; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbrequest_h__ diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index b210e7bda2f6..219dec7bf70e 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -4,374 +4,458 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "base/basictypes.h" + #include "IDBTransaction.h" -#include "BackgroundChildImpl.h" -#include "IDBDatabase.h" -#include "IDBEvents.h" -#include "IDBObjectStore.h" -#include "IDBRequest.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/EventDispatcher.h" -#include "mozilla/dom/DOMError.h" -#include "mozilla/dom/DOMStringList.h" -#include "mozilla/ipc/BackgroundChild.h" #include "nsIAppShell.h" -#include "nsIDOMFile.h" +#include "nsIScriptContext.h" + +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/storage.h" +#include "nsDOMClassInfoID.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/EventDispatcher.h" #include "nsPIDOMWindow.h" -#include "nsServiceManagerUtils.h" -#include "nsTHashtable.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" #include "nsWidgetsCID.h" + +#include "AsyncConnectionHelper.h" +#include "DatabaseInfo.h" +#include "IDBCursor.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IDBObjectStore.h" +#include "IndexedDatabaseManager.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" +#include "TransactionThreadPool.h" -// Include this last to avoid path problems on Windows. -#include "ActorsChild.h" +#include "ipc/IndexedDBChild.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +#define SAVEPOINT_NAME "savepoint" + +using namespace mozilla; +using namespace mozilla::dom; +USING_INDEXEDDB_NAMESPACE +using mozilla::dom::quota::QuotaManager; +using mozilla::ErrorResult; namespace { NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); +#ifdef MOZ_ENABLE_PROFILER_SPS +uint64_t gNextTransactionSerialNumber = 1; +#endif + +PLDHashOperator +DoomCachedStatements(const nsACString& aQuery, + nsCOMPtr& aStatement, + void* aUserArg) +{ + CommitHelper* helper = static_cast(aUserArg); + helper->AddDoomedObject(aStatement); + return PL_DHASH_REMOVE; +} + +// This runnable doesn't actually do anything beyond "prime the pump" and get +// transactions in the right order on the transaction thread pool. +class StartTransactionRunnable : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + + NS_IMETHOD Run() + { + // NOP + return NS_OK; + } +}; + +// Could really use those NS_REFCOUNTING_HAHA_YEAH_RIGHT macros here. +NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::AddRef() +{ + return 2; +} + +NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::Release() +{ + return 1; +} + +NS_IMPL_QUERY_INTERFACE(StartTransactionRunnable, nsIRunnable) + } // anonymous namespace -IDBTransaction::IDBTransaction(IDBDatabase* aDatabase, - const nsTArray& aObjectStoreNames, - Mode aMode) - : IDBWrapperCache(aDatabase) - , mDatabase(aDatabase) - , mObjectStoreNames(aObjectStoreNames) - , mNextObjectStoreId(0) - , mNextIndexId(0) - , mAbortCode(NS_OK) - , mPendingRequestCount(0) - , mReadyState(IDBTransaction::INITIAL) - , mMode(aMode) - , mCreating(false) - , mAbortedByScript(false) +// static +already_AddRefed +IDBTransaction::CreateInternal(IDBDatabase* aDatabase, + const Sequence& aObjectStoreNames, + Mode aMode, + bool aDispatchDelayed, + bool aIsVersionChangeTransactionChild) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess() || !aDispatchDelayed, + "No support for delayed-dispatch transactions in child " + "process!"); + NS_ASSERTION(!aIsVersionChangeTransactionChild || + (!IndexedDatabaseManager::IsMainProcess() && + aMode == IDBTransaction::VERSION_CHANGE), + "Busted logic!"); + + nsRefPtr transaction = new IDBTransaction(aDatabase); + + transaction->SetScriptOwner(aDatabase->GetScriptOwner()); + transaction->mDatabase = aDatabase; + transaction->mMode = aMode; + transaction->mDatabaseInfo = aDatabase->Info(); + transaction->mObjectStoreNames.AppendElements(aObjectStoreNames); + transaction->mObjectStoreNames.Sort(); + + // Remove any duplicate object store names + const uint32_t count = transaction->mObjectStoreNames.Length(); + for (uint32_t index = count - 1; index > 0 && count > 0; index--) { + if (transaction->mObjectStoreNames[index] == + transaction->mObjectStoreNames[index - 1]) { + transaction->mObjectStoreNames.RemoveElementAt(index); + } + } + + IndexedDBTransactionChild* actor = nullptr; + + if (IndexedDatabaseManager::IsMainProcess()) { + if (aMode != IDBTransaction::VERSION_CHANGE) { + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); + NS_ENSURE_TRUE(pool, nullptr); + + static StartTransactionRunnable sStartTransactionRunnable; + pool->Dispatch(transaction, &sStartTransactionRunnable, false, nullptr); + } + } + else if (!aIsVersionChangeTransactionChild) { + IndexedDBDatabaseChild* dbActor = aDatabase->GetActorChild(); + NS_ASSERTION(dbActor, "Must have an actor here!"); + + ipc::NormalTransactionParams params; + params.names().AppendElements(aObjectStoreNames); + params.mode() = aMode; + + actor = new IndexedDBTransactionChild(); + + dbActor->SendPIndexedDBTransactionConstructor(actor, params); + } + + if (!aDispatchDelayed) { + nsCOMPtr appShell = do_GetService(kAppShellCID); + NS_ENSURE_TRUE(appShell, nullptr); + + nsresult rv = appShell->RunBeforeNextEvent(transaction); + NS_ENSURE_SUCCESS(rv, nullptr); + + transaction->mCreating = true; + } + + if (actor) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + actor->SetTransaction(transaction); + } + + return transaction.forget(); +} + +IDBTransaction::IDBTransaction(IDBDatabase* aDatabase) +: IDBWrapperCache(aDatabase), + mReadyState(IDBTransaction::INITIAL), + mMode(IDBTransaction::READ_ONLY), + mPendingRequests(0), + mSavepointCount(0), + mActorChild(nullptr), + mActorParent(nullptr), + mAbortCode(NS_OK), +#ifdef MOZ_ENABLE_PROFILER_SPS + mSerialNumber(gNextTransactionSerialNumber++), +#endif + mCreating(false) #ifdef DEBUG - , mSentCommitOrAbort(false) , mFiredCompleteOrAbort(false) #endif { - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); - - mBackgroundActor.mNormalBackgroundActor = nullptr; - -#ifdef MOZ_ENABLE_PROFILER_SPS - { - using namespace mozilla::ipc; - BackgroundChildImpl::ThreadLocal* threadLocal = - BackgroundChildImpl::GetThreadLocalForCurrentThread(); - MOZ_ASSERT(threadLocal); - - mSerialNumber = threadLocal->mNextTransactionSerialNumber++; - } -#endif - -#ifdef DEBUG - if (!aObjectStoreNames.IsEmpty()) { - nsTArray sortedNames(aObjectStoreNames); - sortedNames.Sort(); - - const uint32_t count = sortedNames.Length(); - MOZ_ASSERT(count == aObjectStoreNames.Length()); - - // Make sure the array is properly sorted. - for (uint32_t index = 0; index < count; index++) { - MOZ_ASSERT(aObjectStoreNames[index] == sortedNames[index]); - } - - // Make sure there are no duplicates in our objectStore names. - for (uint32_t index = 0; index < count - 1; index++) { - MOZ_ASSERT(sortedNames[index] != sortedNames[index + 1]); - } - } -#endif + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } IDBTransaction::~IDBTransaction() { - AssertIsOnOwningThread(); - MOZ_ASSERT(!mPendingRequestCount); - MOZ_ASSERT(!mCreating); - MOZ_ASSERT(mSentCommitOrAbort); - MOZ_ASSERT_IF(mMode == VERSION_CHANGE && - mBackgroundActor.mVersionChangeBackgroundActor, - mFiredCompleteOrAbort); - MOZ_ASSERT_IF(mMode != VERSION_CHANGE && - mBackgroundActor.mNormalBackgroundActor, - mFiredCompleteOrAbort); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mPendingRequests, "Should have no pending requests here!"); + NS_ASSERTION(!mSavepointCount, "Should have released them all!"); + NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!"); + NS_ASSERTION(!mCreating, "Should have been cleared already!"); + NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!"); - mDatabase->UnregisterTransaction(this); - - if (mMode == VERSION_CHANGE) { - if (mBackgroundActor.mVersionChangeBackgroundActor) { - mBackgroundActor.mVersionChangeBackgroundActor->SendDeleteMeInternal(); - MOZ_ASSERT(!mBackgroundActor.mVersionChangeBackgroundActor, - "SendDeleteMeInternal should have cleared!"); - } - } else if (mBackgroundActor.mNormalBackgroundActor) { - mBackgroundActor.mNormalBackgroundActor->SendDeleteMeInternal(); - MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor, - "SendDeleteMeInternal should have cleared!"); - } -} - -// static -already_AddRefed -IDBTransaction::CreateVersionChange( - IDBDatabase* aDatabase, - BackgroundVersionChangeTransactionChild* aActor, - int64_t aNextObjectStoreId, - int64_t aNextIndexId) -{ - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); - MOZ_ASSERT(aActor); - MOZ_ASSERT(aNextObjectStoreId > 0); - MOZ_ASSERT(aNextIndexId > 0); - - nsTArray emptyObjectStoreNames; - - nsRefPtr transaction = - new IDBTransaction(aDatabase, emptyObjectStoreNames, VERSION_CHANGE); - - transaction->SetScriptOwner(aDatabase->GetScriptOwner()); - transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor; - transaction->mNextObjectStoreId = aNextObjectStoreId; - transaction->mNextIndexId = aNextIndexId; - - // XXX Fix! - MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!"); - - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (NS_WARN_IF(!appShell) || - NS_WARN_IF(NS_FAILED(appShell->RunBeforeNextEvent(transaction)))) { - return nullptr; - } - - transaction->mCreating = true; - - aDatabase->RegisterTransaction(transaction); - - return transaction.forget(); -} - -// static -already_AddRefed -IDBTransaction::Create(IDBDatabase* aDatabase, - const nsTArray& aObjectStoreNames, - Mode aMode) -{ - MOZ_ASSERT(aDatabase); - aDatabase->AssertIsOnOwningThread(); - MOZ_ASSERT(!aObjectStoreNames.IsEmpty()); - MOZ_ASSERT(aMode == READ_ONLY || aMode == READ_WRITE); - - nsRefPtr transaction = - new IDBTransaction(aDatabase, aObjectStoreNames, aMode); - - transaction->SetScriptOwner(aDatabase->GetScriptOwner()); - - // XXX Fix! - MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!"); - - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (NS_WARN_IF(!appShell) || - NS_WARN_IF(NS_FAILED(appShell->RunBeforeNextEvent(transaction)))) { - return nullptr; - } - - transaction->mCreating = true; - - aDatabase->RegisterTransaction(transaction); - - return transaction.forget(); -} - -// static -IDBTransaction* -IDBTransaction::GetCurrent() -{ - using namespace mozilla::ipc; - - MOZ_ASSERT(BackgroundChild::GetForCurrentThread()); - - BackgroundChildImpl::ThreadLocal* threadLocal = - BackgroundChildImpl::GetThreadLocalForCurrentThread(); - MOZ_ASSERT(threadLocal); - - return threadLocal->mCurrentTransaction; -} - -#ifdef DEBUG - -void -IDBTransaction::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mDatabase); - mDatabase->AssertIsOnOwningThread(); -} - -#endif // DEBUG - -void -IDBTransaction::SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor); - MOZ_ASSERT(mMode != VERSION_CHANGE); - - mBackgroundActor.mNormalBackgroundActor = aBackgroundActor; -} - -void -IDBTransaction::StartRequest(BackgroundRequestChild* aBackgroundActor, - const RequestParams& aParams) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(aParams.type() != RequestParams::T__None); - - if (mMode == VERSION_CHANGE) { - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - - mBackgroundActor.mVersionChangeBackgroundActor-> - SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams); - } else { - MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); - - mBackgroundActor.mNormalBackgroundActor-> - SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams); - } -} - -void -IDBTransaction::OpenCursor(BackgroundCursorChild* aBackgroundActor, - const OpenCursorParams& aParams) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); - - if (mMode == VERSION_CHANGE) { - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - - mBackgroundActor.mVersionChangeBackgroundActor-> - SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams); - } else { - MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); - - mBackgroundActor.mNormalBackgroundActor-> - SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams); - } - - // Balanced in BackgroundCursorChild::RecvResponse(). - OnNewRequest(); -} - -void -IDBTransaction::RefreshSpec(bool aMayDelete) -{ - AssertIsOnOwningThread(); - - for (uint32_t count = mObjectStores.Length(), index = 0; - index < count; - index++) { - mObjectStores[index]->RefreshSpec(aMayDelete); - } - - for (uint32_t count = mDeletedObjectStores.Length(), index = 0; - index < count; - index++) { - mDeletedObjectStores[index]->RefreshSpec(false); + NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->Send__delete__(mActorChild); + NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); } } void IDBTransaction::OnNewRequest() { - AssertIsOnOwningThread(); - - if (!mPendingRequestCount) { - MOZ_ASSERT(INITIAL == mReadyState); - mReadyState = LOADING; + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (!mPendingRequests) { + NS_ASSERTION(mReadyState == IDBTransaction::INITIAL, + "Reusing a transaction!"); + mReadyState = IDBTransaction::LOADING; } - - ++mPendingRequestCount; + ++mPendingRequests; } void IDBTransaction::OnRequestFinished() { - AssertIsOnOwningThread(); - MOZ_ASSERT(mPendingRequestCount); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mPendingRequests, "Mismatched calls!"); + --mPendingRequests; + if (!mPendingRequests) { + NS_ASSERTION(NS_FAILED(mAbortCode) || mReadyState == IDBTransaction::LOADING, + "Bad state!"); + mReadyState = IDBTransaction::COMMITTING; + CommitOrRollback(); + } +} - --mPendingRequestCount; +void +IDBTransaction::OnRequestDisconnected() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mPendingRequests, "Mismatched calls!"); + --mPendingRequests; +} - if (!mPendingRequestCount && !mDatabase->IsInvalidated()) { - mReadyState = COMMITTING; +void +IDBTransaction::RemoveObjectStore(const nsAString& aName) +{ + NS_ASSERTION(mMode == IDBTransaction::VERSION_CHANGE, + "Only remove object stores on VERSION_CHANGE transactions"); - if (NS_SUCCEEDED(mAbortCode)) { - SendCommit(); - } else { - SendAbort(mAbortCode); + mDatabaseInfo->RemoveObjectStore(aName); + + for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) { + if (mCreatedObjectStores[i]->Name() == aName) { + nsRefPtr objectStore = mCreatedObjectStores[i]; + mCreatedObjectStores.RemoveElementAt(i); + mDeletedObjectStores.AppendElement(objectStore); + break; } } } void -IDBTransaction::SendCommit() +IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener) { - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_SUCCEEDED(mAbortCode)); - MOZ_ASSERT(IsFinished()); - MOZ_ASSERT(!mSentCommitOrAbort); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!mListener, "Shouldn't already have a listener!"); + mListener = aListener; +} - if (mMode == VERSION_CHANGE) { - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - mBackgroundActor.mVersionChangeBackgroundActor->SendCommit(); - } else { - MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); - mBackgroundActor.mNormalBackgroundActor->SendCommit(); +nsresult +IDBTransaction::CommitOrRollback() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (!IndexedDatabaseManager::IsMainProcess()) { + if (mActorChild) { + mActorChild->SendAllRequestsFinished(); + } + + return NS_OK; } -#ifdef DEBUG - mSentCommitOrAbort = true; -#endif + nsRefPtr helper = + new CommitHelper(this, mListener, mCreatedObjectStores); + + TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); + NS_ENSURE_STATE(pool); + + mCachedStatements.Enumerate(DoomCachedStatements, helper); + NS_ASSERTION(!mCachedStatements.Count(), "Statements left!"); + + nsresult rv = pool->Dispatch(this, helper, true, helper); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +bool +IDBTransaction::StartSavepoint() +{ + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); + NS_PRECONDITION(mConnection, "No connection!"); + + nsCOMPtr stmt = GetCachedStatement(NS_LITERAL_CSTRING( + "SAVEPOINT " SAVEPOINT_NAME + )); + NS_ENSURE_TRUE(stmt, false); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, false); + + if (IsWriteAllowed()) { + mUpdateFileRefcountFunction->StartSavepoint(); + } + + ++mSavepointCount; + + return true; +} + +nsresult +IDBTransaction::ReleaseSavepoint() +{ + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); + NS_PRECONDITION(mConnection, "No connection!"); + + NS_ASSERTION(mSavepointCount, "Mismatch!"); + + nsCOMPtr stmt = GetCachedStatement(NS_LITERAL_CSTRING( + "RELEASE SAVEPOINT " SAVEPOINT_NAME + )); + NS_ENSURE_TRUE(stmt, NS_OK); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, NS_OK); + + if (IsWriteAllowed()) { + mUpdateFileRefcountFunction->ReleaseSavepoint(); + } + + --mSavepointCount; + + return NS_OK; } void -IDBTransaction::SendAbort(nsresult aResultCode) +IDBTransaction::RollbackSavepoint() { - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aResultCode)); - MOZ_ASSERT(IsFinished()); - MOZ_ASSERT(!mSentCommitOrAbort); + NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); + NS_PRECONDITION(mConnection, "No connection!"); - if (mMode == VERSION_CHANGE) { - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - mBackgroundActor.mVersionChangeBackgroundActor->SendAbort(aResultCode); - } else { - MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); - mBackgroundActor.mNormalBackgroundActor->SendAbort(aResultCode); + NS_ASSERTION(mSavepointCount == 1, "Mismatch!"); + mSavepointCount = 0; + + nsCOMPtr stmt = GetCachedStatement(NS_LITERAL_CSTRING( + "ROLLBACK TO SAVEPOINT " SAVEPOINT_NAME + )); + NS_ENSURE_TRUE_VOID(stmt); + + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->Execute(); + NS_ENSURE_SUCCESS_VOID(rv); + + if (IsWriteAllowed()) { + mUpdateFileRefcountFunction->RollbackSavepoint(); + } +} + +nsresult +IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("IDBTransaction", "GetOrCreateConnection", + js::ProfileEntry::Category::STORAGE); + + if (mDatabase->IsInvalidated()) { + return NS_ERROR_NOT_AVAILABLE; } + if (!mConnection) { + nsCOMPtr connection = + IDBFactory::GetConnection(mDatabase->FilePath(), mDatabase->Type(), + mDatabase->Group(), mDatabase->Origin()); + NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE); + + nsresult rv; + + nsRefPtr function; + nsCString beginTransaction; + if (mMode != IDBTransaction::READ_ONLY) { + function = new UpdateRefcountFunction(Database()->Manager()); + NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY); + + rv = connection->CreateFunction( + NS_LITERAL_CSTRING("update_refcount"), 2, function); + NS_ENSURE_SUCCESS(rv, rv); + + beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;"); + } + else { + beginTransaction.AssignLiteral("BEGIN TRANSACTION;"); + } + + nsCOMPtr stmt; + rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + function.swap(mUpdateFileRefcountFunction); + connection.swap(mConnection); + } + + nsCOMPtr result(mConnection); + result.forget(aResult); + return NS_OK; +} + +already_AddRefed +IDBTransaction::GetCachedStatement(const nsACString& aQuery) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!aQuery.IsEmpty(), "Empty sql statement!"); + NS_ASSERTION(mConnection, "No connection!"); + + nsCOMPtr stmt; + + if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) { + nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt)); #ifdef DEBUG - mSentCommitOrAbort = true; + if (NS_FAILED(rv)) { + nsCString error; + error.AppendLiteral("The statement `"); + error.Append(aQuery); + error.AppendLiteral("` failed to compile with the error message `"); + nsCString msg; + (void)mConnection->GetLastErrorString(msg); + error.Append(msg); + error.AppendLiteral("`."); + NS_ERROR(error.get()); + } #endif + NS_ENSURE_SUCCESS(rv, nullptr); + + mCachedStatements.Put(aQuery, stmt); + } + + return stmt.forget(); } bool IDBTransaction::IsOpen() const { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); // If we haven't started anything then we're open. if (mReadyState == IDBTransaction::INITIAL) { @@ -383,449 +467,268 @@ IDBTransaction::IsOpen() const // from the time we were created) then we are open. Otherwise check the // currently running transaction to see if it's the same. We only allow other // requests to be made if this transaction is currently running. - if (mReadyState == IDBTransaction::LOADING && - (mCreating || GetCurrent() == this)) { - return true; + if (mReadyState == IDBTransaction::LOADING) { + if (mCreating) { + return true; + } + + if (AsyncConnectionHelper::GetCurrentTransaction() == this) { + return true; + } } return false; } already_AddRefed -IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec) +IDBTransaction::GetOrCreateObjectStore(const nsAString& aName, + ObjectStoreInfo* aObjectStoreInfo, + bool aCreating) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aSpec.metadata().id()); - MOZ_ASSERT(VERSION_CHANGE == mMode); - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - MOZ_ASSERT(IsOpen()); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aObjectStoreInfo, "Null pointer!"); + NS_ASSERTION(!aCreating || GetMode() == IDBTransaction::VERSION_CHANGE, + "How else can we create here?!"); -#ifdef DEBUG - { - const nsString& name = aSpec.metadata().name(); + nsRefPtr retval; - for (uint32_t count = mObjectStores.Length(), index = 0; - index < count; - index++) { - MOZ_ASSERT(mObjectStores[index]->Name() != name); + for (uint32_t index = 0; index < mCreatedObjectStores.Length(); index++) { + nsRefPtr& objectStore = mCreatedObjectStores[index]; + if (objectStore->Name() == aName) { + retval = objectStore; + return retval.forget(); } } -#endif - MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> - SendCreateObjectStore(aSpec.metadata())); + retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id, + aCreating); - nsRefPtr objectStore = IDBObjectStore::Create(this, aSpec); - MOZ_ASSERT(objectStore); + mCreatedObjectStores.AppendElement(retval); - mObjectStores.AppendElement(objectStore); - - return objectStore.forget(); + return retval.forget(); } -void -IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId) +already_AddRefed +IDBTransaction::GetFileInfo(nsIDOMBlob* aBlob) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aObjectStoreId); - MOZ_ASSERT(VERSION_CHANGE == mMode); - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - MOZ_ASSERT(IsOpen()); - - MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> - SendDeleteObjectStore(aObjectStoreId)); - - for (uint32_t count = mObjectStores.Length(), index = 0; - index < count; - index++) { - nsRefPtr& objectStore = mObjectStores[index]; - - if (objectStore->Id() == aObjectStoreId) { - objectStore->NoteDeletion(); - - nsRefPtr* deletedObjectStore = - mDeletedObjectStores.AppendElement(); - deletedObjectStore->swap(mObjectStores[index]); - - mObjectStores.RemoveElementAt(index); - break; - } - } + nsRefPtr fileInfo; + mCreatedFileInfos.Get(aBlob, getter_AddRefs(fileInfo)); + return fileInfo.forget(); } void -IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore, - const IndexMetadata& aMetadata) +IDBTransaction::AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aObjectStore); - MOZ_ASSERT(aMetadata.id()); - MOZ_ASSERT(VERSION_CHANGE == mMode); - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - MOZ_ASSERT(IsOpen()); - - MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> - SendCreateIndex(aObjectStore->Id(), aMetadata)); + mCreatedFileInfos.Put(aBlob, aFileInfo); } void -IDBTransaction::DeleteIndex(IDBObjectStore* aObjectStore, - int64_t aIndexId) +IDBTransaction::ClearCreatedFileInfos() { - AssertIsOnOwningThread(); - MOZ_ASSERT(aObjectStore); - MOZ_ASSERT(aIndexId); - MOZ_ASSERT(VERSION_CHANGE == mMode); - MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); - MOZ_ASSERT(IsOpen()); - - MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> - SendDeleteIndex(aObjectStore->Id(), aIndexId)); + mCreatedFileInfos.Clear(); } -void +nsresult IDBTransaction::AbortInternal(nsresult aAbortCode, already_AddRefed aError) { - AssertIsOnOwningThread(); - MOZ_ASSERT(NS_FAILED(aAbortCode)); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsRefPtr error = aError; if (IsFinished()) { - // Already finished, nothing to do here. - return; + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } - const bool isVersionChange = mMode == VERSION_CHANGE; - const bool isInvalidated = mDatabase->IsInvalidated(); - bool needToSendAbort = mReadyState == INITIAL && !isInvalidated; - -#ifdef DEBUG - if (isInvalidated) { - mSentCommitOrAbort = true; + if (mActorChild) { + NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + mActorChild->SendAbort(aAbortCode); } -#endif + + bool needToCommitOrRollback = mReadyState == IDBTransaction::INITIAL; mAbortCode = aAbortCode; - mReadyState = DONE; + mReadyState = IDBTransaction::DONE; mError = error.forget(); - if (isVersionChange) { + if (GetMode() == IDBTransaction::VERSION_CHANGE) { // If a version change transaction is aborted, we must revert the world - // back to its previous state unless we're being invalidated after the - // transaction already completed. - if (!isInvalidated) { - mDatabase->RevertToPreviousState(); + // back to its previous state. + mDatabase->RevertToPreviousState(); + + DatabaseInfo* dbInfo = mDatabase->Info(); + + for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) { + nsRefPtr& objectStore = mCreatedObjectStores[i]; + ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); + + if (!info) { + info = new ObjectStoreInfo(*objectStore->Info()); + info->indexes.Clear(); + } + + objectStore->SetInfo(info); } - const nsTArray& specArray = - mDatabase->Spec()->objectStores(); + for (uint32_t i = 0; i < mDeletedObjectStores.Length(); i++) { + nsRefPtr& objectStore = mDeletedObjectStores[i]; + ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); - if (specArray.IsEmpty()) { - mObjectStores.Clear(); - mDeletedObjectStores.Clear(); - } else { - nsTHashtable validIds(specArray.Length()); - - for (uint32_t specCount = specArray.Length(), specIndex = 0; - specIndex < specCount; - specIndex++) { - const int64_t objectStoreId = specArray[specIndex].metadata().id(); - MOZ_ASSERT(objectStoreId); - - validIds.PutEntry(uint64_t(objectStoreId)); + if (!info) { + info = new ObjectStoreInfo(*objectStore->Info()); + info->indexes.Clear(); } - for (uint32_t objCount = mObjectStores.Length(), objIndex = 0; - objIndex < objCount; - /* incremented conditionally */) { - const int64_t objectStoreId = mObjectStores[objIndex]->Id(); - MOZ_ASSERT(objectStoreId); - - if (validIds.Contains(uint64_t(objectStoreId))) { - objIndex++; - } else { - mObjectStores.RemoveElementAt(objIndex); - objCount--; - } - } - - if (!mDeletedObjectStores.IsEmpty()) { - for (uint32_t objCount = mDeletedObjectStores.Length(), objIndex = 0; - objIndex < objCount; - objIndex++) { - const int64_t objectStoreId = mDeletedObjectStores[objIndex]->Id(); - MOZ_ASSERT(objectStoreId); - - if (validIds.Contains(uint64_t(objectStoreId))) { - nsRefPtr* objectStore = - mObjectStores.AppendElement(); - objectStore->swap(mDeletedObjectStores[objIndex]); - } - } - mDeletedObjectStores.Clear(); - } + objectStore->SetInfo(info); } + + // and then the db must be closed + mDatabase->Close(); } // Fire the abort event if there are no outstanding requests. Otherwise the // abort event will be fired when all outstanding requests finish. - if (needToSendAbort) { - SendAbort(aAbortCode); + if (needToCommitOrRollback) { + return CommitOrRollback(); } - if (isVersionChange) { - mDatabase->Close(); - } + return NS_OK; } -void +nsresult IDBTransaction::Abort(IDBRequest* aRequest) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aRequest); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aRequest, "This is undesirable."); ErrorResult rv; nsRefPtr error = aRequest->GetError(rv); - AbortInternal(aRequest->GetErrorCode(), error.forget()); + return AbortInternal(aRequest->GetErrorCode(), error.forget()); } -void +nsresult IDBTransaction::Abort(nsresult aErrorCode) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsRefPtr error = new DOMError(GetOwner(), aErrorCode); - AbortInternal(aErrorCode, error.forget()); + return AbortInternal(aErrorCode, error.forget()); } -void -IDBTransaction::Abort(ErrorResult& aRv) -{ - AssertIsOnOwningThread(); - - if (IsFinished()) { - aRv = NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - return; - } - - AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr); - - MOZ_ASSERT(!mAbortedByScript); - mAbortedByScript = true; -} - -void -IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mFiredCompleteOrAbort); - - IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", - "IDBTransaction[%llu] MT Complete", - mTransaction->GetSerialNumber(), mAbortCode); - - mReadyState = DONE; - -#ifdef DEBUG - mFiredCompleteOrAbort = true; -#endif - - nsCOMPtr event; - if (NS_SUCCEEDED(aResult)) { - event = CreateGenericEvent(this, - nsDependentString(kCompleteEventType), - eDoesNotBubble, - eNotCancelable); - } else { - if (!mError && !mAbortedByScript) { - mError = new DOMError(GetOwner(), aResult); - } - - event = CreateGenericEvent(this, - nsDependentString(kAbortEventType), - eDoesBubble, - eNotCancelable); - } - - if (NS_WARN_IF(!event)) { - return; - } - - bool dummy; - if (NS_FAILED(DispatchEvent(event, &dummy))) { - NS_WARNING("DispatchEvent failed!"); - } - - mDatabase->DelayedMaybeExpireFileActors(); -} - -int64_t -IDBTransaction::NextObjectStoreId() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(VERSION_CHANGE == mMode); - - return mNextObjectStoreId++; -} - -int64_t -IDBTransaction::NextIndexId() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(VERSION_CHANGE == mMode); - - return mNextIndexId++; -} - -nsPIDOMWindow* -IDBTransaction::GetParentObject() const -{ - AssertIsOnOwningThread(); - - return mDatabase->GetParentObject(); -} - -IDBTransactionMode -IDBTransaction::GetMode(ErrorResult& aRv) const -{ - AssertIsOnOwningThread(); - - switch (mMode) { - case READ_ONLY: - return IDBTransactionMode::Readonly; - - case READ_WRITE: - return IDBTransactionMode::Readwrite; - - case VERSION_CHANGE: - return IDBTransactionMode::Versionchange; - - case MODE_INVALID: - default: - MOZ_CRASH("Bad mode!"); - } -} - -DOMError* -IDBTransaction::GetError() const -{ - AssertIsOnOwningThread(); - - return mError; -} - -already_AddRefed -IDBTransaction::ObjectStoreNames() -{ - AssertIsOnOwningThread(); - - if (mMode == IDBTransaction::VERSION_CHANGE) { - return mDatabase->ObjectStoreNames(); - } - - nsRefPtr list = new DOMStringList(); - list->StringArray() = mObjectStoreNames; - return list.forget(); -} - -already_AddRefed -IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv) -{ - AssertIsOnOwningThread(); - - if (IsFinished()) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; - } - - const ObjectStoreSpec* spec = nullptr; - - if (IDBTransaction::VERSION_CHANGE == mMode || - mObjectStoreNames.Contains(aName)) { - const nsTArray& objectStores = - mDatabase->Spec()->objectStores(); - - for (uint32_t count = objectStores.Length(), index = 0; - index < count; - index++) { - const ObjectStoreSpec& objectStore = objectStores[index]; - if (objectStore.metadata().name() == aName) { - spec = &objectStore; - break; - } - } - } - - if (!spec) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); - return nullptr; - } - - const int64_t desiredId = spec->metadata().id(); - - nsRefPtr objectStore; - - for (uint32_t count = mObjectStores.Length(), index = 0; - index < count; - index++) { - nsRefPtr& existingObjectStore = mObjectStores[index]; - - if (existingObjectStore->Id() == desiredId) { - objectStore = existingObjectStore; - break; - } - } - - if (!objectStore) { - objectStore = IDBObjectStore::Create(this, *spec); - MOZ_ASSERT(objectStore); - - mObjectStores.AppendElement(objectStore); - } - - return objectStore.forget(); -} - -NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) - NS_INTERFACE_MAP_ENTRY(nsIRunnable) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) - NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStores) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCreatedObjectStores) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) // Don't unlink mDatabase! NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mObjectStores) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCreatedObjectStores) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores) NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) + NS_INTERFACE_MAP_ENTRY(nsIRunnable) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) + +NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) + JSObject* IDBTransaction::WrapObject(JSContext* aCx) { - AssertIsOnOwningThread(); - return IDBTransactionBinding::Wrap(aCx, this); } +mozilla::dom::IDBTransactionMode +IDBTransaction::GetMode(ErrorResult& aRv) const +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + switch (mMode) { + case READ_ONLY: + return mozilla::dom::IDBTransactionMode::Readonly; + + case READ_WRITE: + return mozilla::dom::IDBTransactionMode::Readwrite; + + case VERSION_CHANGE: + return mozilla::dom::IDBTransactionMode::Versionchange; + + case MODE_INVALID: + default: + aRv.Throw(NS_ERROR_UNEXPECTED); + return mozilla::dom::IDBTransactionMode::Readonly; + } +} + +DOMError* +IDBTransaction::GetError() const +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return mError; +} + +already_AddRefed +IDBTransaction::GetObjectStoreNames(ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + nsRefPtr list(new DOMStringList()); + + if (mMode == IDBTransaction::VERSION_CHANGE) { + mDatabaseInfo->GetObjectStoreNames(list->StringArray()); + } + else { + list->StringArray() = mObjectStoreNames; + } + + return list.forget(); +} + +already_AddRefed +IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + if (IsFinished()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + ObjectStoreInfo* info = nullptr; + + if (mMode == IDBTransaction::VERSION_CHANGE || + mObjectStoreNames.Contains(aName)) { + info = mDatabaseInfo->GetObjectStore(aName); + } + + if (!info) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); + return nullptr; + } + + nsRefPtr objectStore = + GetOrCreateObjectStore(aName, info, false); + if (!objectStore) { + IDB_WARNING("Failed to get or create object store!"); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + + return objectStore.forget(); +} + nsresult IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) { - AssertIsOnOwningThread(); - aVisitor.mCanHandle = true; aVisitor.mParentTarget = mDatabase; return NS_OK; @@ -834,21 +737,545 @@ IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) NS_IMETHODIMP IDBTransaction::Run() { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); // We're back at the event loop, no longer newborn. mCreating = false; - // Maybe commit if there were no requests generated. + // Maybe set the readyState to DONE if there were no requests generated. if (mReadyState == IDBTransaction::INITIAL) { - mReadyState = DONE; + mReadyState = IDBTransaction::DONE; - SendCommit(); + if (NS_FAILED(CommitOrRollback())) { + NS_WARNING("Failed to commit!"); + } } return NS_OK; } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +CommitHelper::CommitHelper( + IDBTransaction* aTransaction, + IDBTransactionListener* aListener, + const nsTArray >& aUpdatedObjectStores) +: mTransaction(aTransaction), + mListener(aListener), + mAbortCode(aTransaction->mAbortCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mConnection.swap(aTransaction->mConnection); + mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction); + + for (uint32_t i = 0; i < aUpdatedObjectStores.Length(); i++) { + ObjectStoreInfo* info = aUpdatedObjectStores[i]->Info(); + if (info->comittedAutoIncrementId != info->nextAutoIncrementId) { + mAutoIncrementObjectStores.AppendElement(aUpdatedObjectStores[i]); + } + } +} + +CommitHelper::CommitHelper(IDBTransaction* aTransaction, + nsresult aAbortCode) +: mTransaction(aTransaction), + mAbortCode(aAbortCode) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +} + +CommitHelper::~CommitHelper() +{ +} + +NS_IMPL_ISUPPORTS(CommitHelper, nsIRunnable) + +NS_IMETHODIMP +CommitHelper::Run() +{ + if (NS_IsMainThread()) { + PROFILER_MAIN_THREAD_LABEL("CommitHelper", "Run", + js::ProfileEntry::Category::STORAGE); + + NS_ASSERTION(mDoomedObjects.IsEmpty(), "Didn't release doomed objects!"); + + mTransaction->mReadyState = IDBTransaction::DONE; + + // Release file infos on the main thread, so they will eventually get + // destroyed on correct thread. + mTransaction->ClearCreatedFileInfos(); + if (mUpdateFileRefcountFunction) { + mUpdateFileRefcountFunction->ClearFileInfoEntries(); + mUpdateFileRefcountFunction = nullptr; + } + + nsCOMPtr event; + if (NS_FAILED(mAbortCode)) { + if (mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) { + // This will make the database take a snapshot of it's DatabaseInfo + mTransaction->Database()->Close(); + // Then remove the info from the hash as it contains invalid data. + DatabaseInfo::Remove(mTransaction->Database()->Id()); + } + + event = CreateGenericEvent(mTransaction, + NS_LITERAL_STRING(ABORT_EVT_STR), + eDoesBubble, eNotCancelable); + + // The transaction may already have an error object (e.g. if one of the + // requests failed). If it doesn't, and it wasn't aborted + // programmatically, create one now. + if (!mTransaction->mError && + mAbortCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { + mTransaction->mError = new DOMError(mTransaction->GetOwner(), mAbortCode); + } + } + else { + event = CreateGenericEvent(mTransaction, + NS_LITERAL_STRING(COMPLETE_EVT_STR), + eDoesNotBubble, eNotCancelable); + } + IDB_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mListener) { + mListener->NotifyTransactionPreComplete(mTransaction); + } + + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", + "IDBTransaction[%llu] MT Complete", + mTransaction->GetSerialNumber(), mAbortCode); + + bool dummy; + if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) { + NS_WARNING("Dispatch failed!"); + } + +#ifdef DEBUG + mTransaction->mFiredCompleteOrAbort = true; +#endif + + if (mListener) { + mListener->NotifyTransactionPostComplete(mTransaction); + } + + mTransaction = nullptr; + + return NS_OK; + } + + PROFILER_LABEL("CommitHelper", "Run", + js::ProfileEntry::Category::STORAGE); + + IDBDatabase* database = mTransaction->Database(); + if (database->IsInvalidated()) { + IDB_REPORT_INTERNAL_ERR(); + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (mConnection) { + QuotaManager::SetCurrentWindow(database->GetOwner()); + + if (NS_SUCCEEDED(mAbortCode) && mUpdateFileRefcountFunction && + NS_FAILED(mUpdateFileRefcountFunction->WillCommit(mConnection))) { + IDB_REPORT_INTERNAL_ERR(); + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_SUCCEEDED(mAbortCode) && NS_FAILED(WriteAutoIncrementCounts())) { + IDB_REPORT_INTERNAL_ERR(); + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_SUCCEEDED(mAbortCode)) { + NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION"); + nsresult rv = mConnection->ExecuteSimpleSQL(release); + if (NS_SUCCEEDED(rv)) { + if (mUpdateFileRefcountFunction) { + mUpdateFileRefcountFunction->DidCommit(); + } + CommitAutoIncrementCounts(); + } + else if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, + // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. + mAbortCode = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + else { + IDB_REPORT_INTERNAL_ERR(); + mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + + if (NS_FAILED(mAbortCode)) { + if (mUpdateFileRefcountFunction) { + mUpdateFileRefcountFunction->DidAbort(); + } + RevertAutoIncrementCounts(); + NS_NAMED_LITERAL_CSTRING(rollback, "ROLLBACK TRANSACTION"); + if (NS_FAILED(mConnection->ExecuteSimpleSQL(rollback))) { + NS_WARNING("Failed to rollback transaction!"); + } + } + } + + mDoomedObjects.Clear(); + + if (mConnection) { + if (mUpdateFileRefcountFunction) { + nsresult rv = mConnection->RemoveFunction( + NS_LITERAL_CSTRING("update_refcount")); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to remove function!"); + } + } + + mConnection->Close(); + mConnection = nullptr; + + QuotaManager::SetCurrentWindow(nullptr); + } + + return NS_OK; +} + +nsresult +CommitHelper::WriteAutoIncrementCounts() +{ + nsCOMPtr stmt; + nsresult rv; + for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { + ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); + if (!stmt) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE object_store SET auto_increment = :ai " + "WHERE id = :osid;"), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + stmt->Reset(); + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), info->id); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("ai"), + info->nextAutoIncrementId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +void +CommitHelper::CommitAutoIncrementCounts() +{ + for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { + ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); + info->comittedAutoIncrementId = info->nextAutoIncrementId; + } +} + +void +CommitHelper::RevertAutoIncrementCounts() +{ + for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { + ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); + info->nextAutoIncrementId = info->comittedAutoIncrementId; + } +} + +NS_IMPL_ISUPPORTS(UpdateRefcountFunction, mozIStorageFunction) + +NS_IMETHODIMP +UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues, + nsIVariant** _retval) +{ + *_retval = nullptr; + + uint32_t numEntries; + nsresult rv = aValues->GetNumEntries(&numEntries); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(numEntries == 2, "unexpected number of arguments"); + +#ifdef DEBUG + int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL; + aValues->GetTypeOfIndex(0, &type1); + + int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL; + aValues->GetTypeOfIndex(1, &type2); + + NS_ASSERTION(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL && + type2 == mozIStorageValueArray::VALUE_TYPE_NULL), + "Shouldn't be called!"); +#endif + + rv = ProcessValue(aValues, 0, eDecrement); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ProcessValue(aValues, 1, eIncrement); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpdateRefcountFunction::WillCommit(mozIStorageConnection* aConnection) +{ + DatabaseUpdateFunction function(aConnection, this); + + mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function); + + nsresult rv = function.ErrorCode(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CreateJournals(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +UpdateRefcountFunction::DidCommit() +{ + mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nullptr); + + nsresult rv = RemoveJournals(mJournalsToRemoveAfterCommit); + NS_ENSURE_SUCCESS_VOID(rv); +} + +void +UpdateRefcountFunction::DidAbort() +{ + nsresult rv = RemoveJournals(mJournalsToRemoveAfterAbort); + NS_ENSURE_SUCCESS_VOID(rv); +} + +nsresult +UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues, + int32_t aIndex, + UpdateType aUpdateType) +{ + int32_t type; + aValues->GetTypeOfIndex(aIndex, &type); + if (type == mozIStorageValueArray::VALUE_TYPE_NULL) { + return NS_OK; + } + + nsString ids; + aValues->GetString(aIndex, ids); + + nsTArray fileIds; + nsresult rv = IDBObjectStore::ConvertFileIdsToArray(ids, fileIds); + NS_ENSURE_SUCCESS(rv, rv); + + for (uint32_t i = 0; i < fileIds.Length(); i++) { + int64_t id = fileIds.ElementAt(i); + + FileInfoEntry* entry; + if (!mFileInfoEntries.Get(id, &entry)) { + nsRefPtr fileInfo = mFileManager->GetFileInfo(id); + NS_ASSERTION(fileInfo, "Shouldn't be null!"); + + nsAutoPtr newEntry(new FileInfoEntry(fileInfo)); + mFileInfoEntries.Put(id, newEntry); + entry = newEntry.forget(); + } + + if (mInSavepoint) { + mSavepointEntriesIndex.Put(id, entry); + } + + switch (aUpdateType) { + case eIncrement: + entry->mDelta++; + if (mInSavepoint) { + entry->mSavepointDelta++; + } + break; + case eDecrement: + entry->mDelta--; + if (mInSavepoint) { + entry->mSavepointDelta--; + } + break; + default: + NS_NOTREACHED("Unknown update type!"); + } + } + + return NS_OK; +} + +nsresult +UpdateRefcountFunction::CreateJournals() +{ + nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); + NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE); + + for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) { + int64_t id = mJournalsToCreateBeforeCommit[i]; + + nsCOMPtr file = + mFileManager->GetFileForId(journalDirectory, id); + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); + + nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + NS_ENSURE_SUCCESS(rv, rv); + + mJournalsToRemoveAfterAbort.AppendElement(id); + } + + return NS_OK; +} + +nsresult +UpdateRefcountFunction::RemoveJournals(const nsTArray& aJournals) +{ + nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); + NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE); + + for (uint32_t index = 0; index < aJournals.Length(); index++) { + nsCOMPtr file = + mFileManager->GetFileForId(journalDirectory, aJournals[index]); + NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); + + if (NS_FAILED(file->Remove(false))) { + NS_WARNING("Failed to removed journal!"); + } + } + + return NS_OK; +} + +PLDHashOperator +UpdateRefcountFunction::DatabaseUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg) +{ + if (!aValue->mDelta) { + return PL_DHASH_NEXT; + } + + DatabaseUpdateFunction* function = + static_cast(aUserArg); + + if (!function->Update(aKey, aValue->mDelta)) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; +} + +PLDHashOperator +UpdateRefcountFunction::FileInfoUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg) +{ + if (aValue->mDelta) { + aValue->mFileInfo->UpdateDBRefs(aValue->mDelta); + } + + return PL_DHASH_NEXT; +} + +PLDHashOperator +UpdateRefcountFunction::RollbackSavepointCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg) +{ + aValue->mDelta -= aValue->mSavepointDelta; + + return PL_DHASH_NEXT; +} + +bool +UpdateRefcountFunction::DatabaseUpdateFunction::Update(int64_t aId, + int32_t aDelta) +{ + nsresult rv = UpdateInternal(aId, aDelta); + if (NS_FAILED(rv)) { + mErrorCode = rv; + return false; + } + + return true; +} + +nsresult +UpdateRefcountFunction::DatabaseUpdateFunction::UpdateInternal(int64_t aId, + int32_t aDelta) +{ + nsresult rv; + + if (!mUpdateStatement) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE file SET refcount = refcount + :delta WHERE id = :id" + ), getter_AddRefs(mUpdateStatement)); + NS_ENSURE_SUCCESS(rv, rv); + } + + mozStorageStatementScoper updateScoper(mUpdateStatement); + + rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mUpdateStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t rows; + rv = mConnection->GetAffectedRows(&rows); + NS_ENSURE_SUCCESS(rv, rv); + + if (rows > 0) { + if (!mSelectStatement) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id FROM file where id = :id" + ), getter_AddRefs(mSelectStatement)); + NS_ENSURE_SUCCESS(rv, rv); + } + + mozStorageStatementScoper selectScoper(mSelectStatement); + + rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasResult; + rv = mSelectStatement->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, rv); + + if (!hasResult) { + // Don't have to create the journal here, we can create all at once, + // just before commit + mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId); + } + + return NS_OK; + } + + if (!mInsertStatement) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO file (id, refcount) VALUES(:id, :delta)" + ), getter_AddRefs(mInsertStatement)); + NS_ENSURE_SUCCESS(rv, rv); + } + + mozStorageStatementScoper insertScoper(mInsertStatement); + + rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mInsertStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId); + + return NS_OK; +} diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index d07d5480a9cb..db8149323e98 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -8,48 +8,71 @@ #define mozilla_dom_indexeddb_idbtransaction_h__ #include "mozilla/Attributes.h" -#include "mozilla/dom/IDBTransactionBinding.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" -#include "nsAutoPtr.h" -#include "nsCycleCollectionParticipant.h" -#include "nsIRunnable.h" -#include "nsString.h" -#include "nsTArray.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" -class nsIDOMBlob; +#include "mozIStorageConnection.h" +#include "mozIStorageStatement.h" +#include "mozIStorageFunction.h" +#include "mozilla/dom/DOMError.h" +#include "nsIRunnable.h" + +#include "nsAutoPtr.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsInterfaceHashtable.h" +#include "nsRefPtrHashtable.h" + +#include "mozilla/dom/IDBTransactionBinding.h" +#include "mozilla/dom/indexedDB/IDBDatabase.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/indexedDB/FileInfo.h" + +class nsIThread; class nsPIDOMWindow; namespace mozilla { - -class ErrorResult; class EventChainPreVisitor; +} // namespace mozilla -namespace dom { +BEGIN_INDEXEDDB_NAMESPACE -class DOMError; -class DOMStringList; -class PBlobChild; - -namespace indexedDB { - -class BackgroundCursorChild; -class BackgroundRequestChild; -class BackgroundTransactionChild; -class BackgroundVersionChangeTransactionChild; -class IDBDatabase; -class IDBObjectStore; +class AsyncConnectionHelper; +class CommitHelper; class IDBRequest; -class IndexMetadata; -class ObjectStoreSpec; -class OpenCursorParams; -class PBackgroundIDBDatabaseFileChild; -class RequestParams; +class IndexedDBDatabaseChild; +class IndexedDBTransactionChild; +class IndexedDBTransactionParent; +struct ObjectStoreInfo; +class TransactionThreadPool; +class UpdateRefcountFunction; -class IDBTransaction MOZ_FINAL - : public IDBWrapperCache - , public nsIRunnable +class IDBTransactionListener { public: + NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0; + NS_IMETHOD_(MozExternalRefCountType) Release() = 0; + + // Called just before dispatching the final events on the transaction. + virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) = 0; + // Called just after dispatching the final events on the transaction. + virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) = 0; +}; + +class IDBTransaction : public IDBWrapperCache, + public nsIRunnable +{ + friend class AsyncConnectionHelper; + friend class CommitHelper; + friend class IndexedDBDatabaseChild; + friend class ThreadObserver; + friend class TransactionThreadPool; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) + enum Mode { READ_ONLY = 0, @@ -68,184 +91,156 @@ public: DONE }; -private: - nsRefPtr mDatabase; - nsRefPtr mError; - nsTArray mObjectStoreNames; - nsTArray> mObjectStores; - nsTArray> mDeletedObjectStores; - - // Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be - // a BackgroundVersionChangeTransactionChild. Otherwise it will be a - // BackgroundTransactionChild. - union { - BackgroundTransactionChild* mNormalBackgroundActor; - BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor; - } mBackgroundActor; - - - // Only used for VERSION_CHANGE transactions. - int64_t mNextObjectStoreId; - int64_t mNextIndexId; - -#ifdef MOZ_ENABLE_PROFILER_SPS - uint64_t mSerialNumber; -#endif - - nsresult mAbortCode; - uint32_t mPendingRequestCount; - - ReadyState mReadyState; - Mode mMode; - - bool mCreating; - bool mAbortedByScript; - -#ifdef DEBUG - bool mSentCommitOrAbort; - bool mFiredCompleteOrAbort; -#endif - -public: - static already_AddRefed - CreateVersionChange(IDBDatabase* aDatabase, - BackgroundVersionChangeTransactionChild* aActor, - int64_t aNextObjectStoreId, - int64_t aNextIndexId); - static already_AddRefed Create(IDBDatabase* aDatabase, - const nsTArray& aObjectStoreNames, - Mode aMode); - - static IDBTransaction* - GetCurrent(); - - void - AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif - - void - SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor); - - void - ClearBackgroundActor() + const Sequence& aObjectStoreNames, + Mode aMode, + bool aDispatchDelayed) { - AssertIsOnOwningThread(); - - if (mMode == VERSION_CHANGE) { - mBackgroundActor.mVersionChangeBackgroundActor = nullptr; - } else { - mBackgroundActor.mNormalBackgroundActor = nullptr; - } + return CreateInternal(aDatabase, aObjectStoreNames, aMode, aDispatchDelayed, + false); } - void - StartRequest(BackgroundRequestChild* aBackgroundActor, - const RequestParams& aParams); + // nsIDOMEventTarget + virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; - void - OpenCursor(BackgroundCursorChild* aBackgroundActor, - const OpenCursorParams& aParams); + void OnNewRequest(); + void OnRequestFinished(); + void OnRequestDisconnected(); - void - RefreshSpec(bool aMayDelete); + void RemoveObjectStore(const nsAString& aName); - void - OnNewRequest(); + void SetTransactionListener(IDBTransactionListener* aListener); - void - OnRequestFinished(); + bool StartSavepoint(); + nsresult ReleaseSavepoint(); + void RollbackSavepoint(); - bool - IsOpen() const; + // Only meant to be called on mStorageThread! + nsresult GetOrCreateConnection(mozIStorageConnection** aConnection); - bool - IsFinished() const + already_AddRefed + GetCachedStatement(const nsACString& aQuery); + + template + already_AddRefed + GetCachedStatement(const char (&aQuery)[N]) + { + return GetCachedStatement(NS_LITERAL_CSTRING(aQuery)); + } + + bool IsOpen() const; + + bool IsFinished() const { - AssertIsOnOwningThread(); return mReadyState > LOADING; } - bool - IsWriteAllowed() const + bool IsWriteAllowed() const { - AssertIsOnOwningThread(); return mMode == READ_WRITE || mMode == VERSION_CHANGE; } - bool - IsAborted() const + bool IsAborted() const { - AssertIsOnOwningThread(); return NS_FAILED(mAbortCode); } // 'Get' prefix is to avoid name collisions with the enum - Mode - GetMode() const + Mode GetMode() { - AssertIsOnOwningThread(); return mMode; } - IDBDatabase* - Database() const + IDBDatabase* Database() { - AssertIsOnOwningThread(); + NS_ASSERTION(mDatabase, "This should never be null!"); return mDatabase; } - IDBDatabase* - Db() const + DatabaseInfo* DBInfo() const { - return Database(); - } - - const nsTArray& - ObjectStoreNamesInternal() const - { - AssertIsOnOwningThread(); - return mObjectStoreNames; + return mDatabaseInfo; } already_AddRefed - CreateObjectStore(const ObjectStoreSpec& aSpec); + GetOrCreateObjectStore(const nsAString& aName, + ObjectStoreInfo* aObjectStoreInfo, + bool aCreating); + + already_AddRefed GetFileInfo(nsIDOMBlob* aBlob); + void AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo); + + void ClearCreatedFileInfos(); void - DeleteObjectStore(int64_t aObjectStoreId); + SetActor(IndexedDBTransactionChild* aActorChild) + { + NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); + mActorChild = aActorChild; + } void - CreateIndex(IDBObjectStore* aObjectStore, const IndexMetadata& aMetadata); + SetActor(IndexedDBTransactionParent* aActorParent) + { + NS_ASSERTION(!aActorParent || !mActorParent, + "Shouldn't have more than one!"); + mActorParent = aActorParent; + } - void - DeleteIndex(IDBObjectStore* aObjectStore, int64_t aIndexId); + IndexedDBTransactionChild* + GetActorChild() const + { + return mActorChild; + } - void + IndexedDBTransactionParent* + GetActorParent() const + { + return mActorParent; + } + + nsresult Abort(IDBRequest* aRequest); - void + nsresult Abort(nsresult aAbortCode); + nsresult + GetAbortCode() const + { + return mAbortCode; + } + #ifdef MOZ_ENABLE_PROFILER_SPS - uint32_t + uint64_t GetSerialNumber() const { - AssertIsOnOwningThread(); return mSerialNumber; } #endif + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // WebIDL nsPIDOMWindow* - GetParentObject() const; + GetParentObject() const + { + return GetOwner(); + } IDBTransactionMode GetMode(ErrorResult& aRv) const; + IDBDatabase* + Db() const + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mDatabase; + } + DOMError* GetError() const; @@ -253,56 +248,255 @@ public: ObjectStore(const nsAString& aName, ErrorResult& aRv); void - Abort(ErrorResult& aRv); + Abort(ErrorResult& aRv) + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + aRv = AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr); + } IMPL_EVENT_HANDLER(abort) IMPL_EVENT_HANDLER(complete) IMPL_EVENT_HANDLER(error) already_AddRefed - ObjectStoreNames(); - - void - FireCompleteOrAbortEvents(nsresult aResult); - - // Only for VERSION_CHANGE transactions. - int64_t - NextObjectStoreId(); - - // Only for VERSION_CHANGE transactions. - int64_t - NextIndexId(); - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIRUNNABLE - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // nsIDOMEventTarget - virtual nsresult - PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; + GetObjectStoreNames(ErrorResult& aRv); private: - IDBTransaction(IDBDatabase* aDatabase, - const nsTArray& aObjectStoreNames, - Mode aMode); + nsresult + AbortInternal(nsresult aAbortCode, + already_AddRefed aError); + + // Should only be called directly through IndexedDBDatabaseChild. + static already_AddRefed + CreateInternal(IDBDatabase* aDatabase, + const Sequence& aObjectStoreNames, + Mode aMode, + bool aDispatchDelayed, + bool aIsVersionChangeTransactionChild); + + explicit IDBTransaction(IDBDatabase* aDatabase); ~IDBTransaction(); - void - AbortInternal(nsresult aAbortCode, already_AddRefed aError); + nsresult CommitOrRollback(); - void - SendCommit(); + nsRefPtr mDatabase; + nsRefPtr mDatabaseInfo; + nsRefPtr mError; + nsTArray mObjectStoreNames; + ReadyState mReadyState; + Mode mMode; + uint32_t mPendingRequests; - void - SendAbort(nsresult aResultCode); + nsInterfaceHashtable + mCachedStatements; + + nsRefPtr mListener; + + // Only touched on the database thread. + nsCOMPtr mConnection; + + // Only touched on the database thread. + uint32_t mSavepointCount; + + nsTArray > mCreatedObjectStores; + nsTArray > mDeletedObjectStores; + + nsRefPtr mUpdateFileRefcountFunction; + nsRefPtrHashtable mCreatedFileInfos; + + IndexedDBTransactionChild* mActorChild; + IndexedDBTransactionParent* mActorParent; + + nsresult mAbortCode; +#ifdef MOZ_ENABLE_PROFILER_SPS + uint64_t mSerialNumber; +#endif + bool mCreating; + +#ifdef DEBUG + bool mFiredCompleteOrAbort; +#endif }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +class CommitHelper MOZ_FINAL : public nsIRunnable +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + + CommitHelper(IDBTransaction* aTransaction, + IDBTransactionListener* aListener, + const nsTArray >& mUpdatedObjectStores); + CommitHelper(IDBTransaction* aTransaction, + nsresult aAbortCode); + + template + bool AddDoomedObject(nsCOMPtr& aCOMPtr) + { + if (aCOMPtr) { + if (!mDoomedObjects.AppendElement(do_QueryInterface(aCOMPtr))) { + NS_ERROR("Out of memory!"); + return false; + } + aCOMPtr = nullptr; + } + return true; + } + +private: + ~CommitHelper(); + + // Writes new autoincrement counts to database + nsresult WriteAutoIncrementCounts(); + + // Updates counts after a successful commit + void CommitAutoIncrementCounts(); + + // Reverts counts when a transaction is aborted + void RevertAutoIncrementCounts(); + + nsRefPtr mTransaction; + nsRefPtr mListener; + nsCOMPtr mConnection; + nsRefPtr mUpdateFileRefcountFunction; + nsAutoTArray, 10> mDoomedObjects; + nsAutoTArray, 10> mAutoIncrementObjectStores; + + nsresult mAbortCode; +}; + +class UpdateRefcountFunction MOZ_FINAL : public mozIStorageFunction +{ + ~UpdateRefcountFunction() + { } + +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_MOZISTORAGEFUNCTION + + explicit UpdateRefcountFunction(FileManager* aFileManager) + : mFileManager(aFileManager), mInSavepoint(false) + { } + + void StartSavepoint() + { + MOZ_ASSERT(!mInSavepoint); + MOZ_ASSERT(!mSavepointEntriesIndex.Count()); + + mInSavepoint = true; + } + + void ReleaseSavepoint() + { + MOZ_ASSERT(mInSavepoint); + + mSavepointEntriesIndex.Clear(); + + mInSavepoint = false; + } + + void RollbackSavepoint() + { + MOZ_ASSERT(mInSavepoint); + + mInSavepoint = false; + + mSavepointEntriesIndex.EnumerateRead(RollbackSavepointCallback, nullptr); + + mSavepointEntriesIndex.Clear(); + } + + void ClearFileInfoEntries() + { + mFileInfoEntries.Clear(); + } + + nsresult WillCommit(mozIStorageConnection* aConnection); + void DidCommit(); + void DidAbort(); + +private: + class FileInfoEntry + { + public: + explicit FileInfoEntry(FileInfo* aFileInfo) + : mFileInfo(aFileInfo), mDelta(0), mSavepointDelta(0) + { } + + ~FileInfoEntry() + { } + + nsRefPtr mFileInfo; + int32_t mDelta; + int32_t mSavepointDelta; + }; + + enum UpdateType { + eIncrement, + eDecrement + }; + + class DatabaseUpdateFunction + { + public: + DatabaseUpdateFunction(mozIStorageConnection* aConnection, + UpdateRefcountFunction* aFunction) + : mConnection(aConnection), mFunction(aFunction), mErrorCode(NS_OK) + { } + + bool Update(int64_t aId, int32_t aDelta); + nsresult ErrorCode() + { + return mErrorCode; + } + + private: + nsresult UpdateInternal(int64_t aId, int32_t aDelta); + + nsCOMPtr mConnection; + nsCOMPtr mUpdateStatement; + nsCOMPtr mSelectStatement; + nsCOMPtr mInsertStatement; + + UpdateRefcountFunction* mFunction; + + nsresult mErrorCode; + }; + + nsresult ProcessValue(mozIStorageValueArray* aValues, + int32_t aIndex, + UpdateType aUpdateType); + + nsresult CreateJournals(); + + nsresult RemoveJournals(const nsTArray& aJournals); + + static PLDHashOperator + DatabaseUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg); + + static PLDHashOperator + FileInfoUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg); + + static PLDHashOperator + RollbackSavepointCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg); + + FileManager* mFileManager; + nsClassHashtable mFileInfoEntries; + nsDataHashtable mSavepointEntriesIndex; + + nsTArray mJournalsToCreateBeforeCommit; + nsTArray mJournalsToRemoveAfterCommit; + nsTArray mJournalsToRemoveAfterAbort; + + bool mInSavepoint; +}; + +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbtransaction_h__ diff --git a/dom/indexedDB/IDBWrapperCache.cpp b/dom/indexedDB/IDBWrapperCache.cpp index 8a7b46249b27..964f2992ea06 100644 --- a/dom/indexedDB/IDBWrapperCache.cpp +++ b/dom/indexedDB/IDBWrapperCache.cpp @@ -5,19 +5,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "IDBWrapperCache.h" - -#include "mozilla/HoldDropJSObjects.h" -#include "nsCOMPtr.h" -#include "nsIScriptGlobalObject.h" -#include "nsPIDOMWindow.h" - -#ifdef DEBUG #include "nsCycleCollector.h" -#endif -namespace mozilla { -namespace dom { -namespace indexedDB { +USING_INDEXEDDB_NAMESPACE NS_IMPL_CYCLE_COLLECTION_CLASS(IDBWrapperCache) @@ -48,14 +38,6 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(IDBWrapperCache, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(IDBWrapperCache, DOMEventTargetHelper) -IDBWrapperCache::IDBWrapperCache(DOMEventTargetHelper* aOwner) - : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) -{ } - -IDBWrapperCache::IDBWrapperCache(nsPIDOMWindow* aOwner) - : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) -{ } - IDBWrapperCache::~IDBWrapperCache() { mScriptOwner = nullptr; @@ -66,7 +48,7 @@ IDBWrapperCache::~IDBWrapperCache() void IDBWrapperCache::SetScriptOwner(JSObject* aScriptOwner) { - MOZ_ASSERT(aScriptOwner); + NS_ASSERTION(aScriptOwner, "This should never be null!"); mScriptOwner = aScriptOwner; mozilla::HoldJSObjects(this); @@ -80,7 +62,3 @@ IDBWrapperCache::AssertIsRooted() const "Why aren't we rooted?!"); } #endif - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/IDBWrapperCache.h b/dom/indexedDB/IDBWrapperCache.h index d77e9dd93e07..129cc0b3129a 100644 --- a/dom/indexedDB/IDBWrapperCache.h +++ b/dom/indexedDB/IDBWrapperCache.h @@ -7,51 +7,47 @@ #ifndef mozilla_dom_indexeddb_idbwrappercache_h__ #define mozilla_dom_indexeddb_idbwrappercache_h__ -#include "js/RootingAPI.h" #include "mozilla/DOMEventTargetHelper.h" -#include "nsCycleCollectionParticipant.h" -#include "nsWrapperCache.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" -class nsPIDOMWindow; - -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE class IDBWrapperCache : public DOMEventTargetHelper { - JS::Heap mScriptOwner; - public: NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBWrapperCache, - DOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( + IDBWrapperCache, + DOMEventTargetHelper) - JSObject* - GetScriptOwner() const + JSObject* GetScriptOwner() const { return mScriptOwner; } + void SetScriptOwner(JSObject* aScriptOwner); - void - SetScriptOwner(JSObject* aScriptOwner); - - void AssertIsRooted() const #ifdef DEBUG - ; + void AssertIsRooted() const; #else - { } + inline void AssertIsRooted() const + { + } #endif protected: - explicit IDBWrapperCache(DOMEventTargetHelper* aOwner); - explicit IDBWrapperCache(nsPIDOMWindow* aOwner); + explicit IDBWrapperCache(DOMEventTargetHelper* aOwner) + : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) + { } + explicit IDBWrapperCache(nsPIDOMWindow* aOwner) + : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) + { } virtual ~IDBWrapperCache(); + +private: + JS::Heap mScriptOwner; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_idbwrappercache_h__ diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index aa583ea1f88b..1afeab71b0de 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -9,70 +9,148 @@ #include "nsIProgrammingLanguage.h" +#include "mozilla/Attributes.h" #include "js/StructuredClone.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsError.h" +#include "nsString.h" #include "nsTArray.h" +#include "nsIInputStream.h" + +#define BEGIN_INDEXEDDB_NAMESPACE \ + namespace mozilla { namespace dom { namespace indexedDB { + +#define END_INDEXEDDB_NAMESPACE \ + } /* namespace indexedDB */ } /* namepsace dom */ } /* namespace mozilla */ + +#define USING_INDEXEDDB_NAMESPACE \ + using namespace mozilla::dom::indexedDB; class nsIDOMBlob; -class nsIInputStream; -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE class FileInfo; class IDBDatabase; class IDBTransaction; -class SerializedStructuredCloneReadInfo; -class SerializedStructuredCloneWriteInfo; struct StructuredCloneFile { + bool operator==(const StructuredCloneFile& aOther) const + { + return this->mFile == aOther.mFile && + this->mFileInfo == aOther.mFileInfo && + this->mInputStream == aOther.mInputStream; + } + nsCOMPtr mFile; nsRefPtr mFileInfo; - - // In IndexedDatabaseInlines.h - inline - StructuredCloneFile(); - - // In IndexedDatabaseInlines.h - inline - ~StructuredCloneFile(); - - // In IndexedDatabaseInlines.h - inline bool - operator==(const StructuredCloneFile& aOther) const; + nsCOMPtr mInputStream; }; +struct SerializedStructuredCloneReadInfo; + struct StructuredCloneReadInfo { - nsTArray mData; + // In IndexedDatabaseInlines.h + inline StructuredCloneReadInfo(); + + inline StructuredCloneReadInfo& + operator=(StructuredCloneReadInfo&& aCloneReadInfo); + + // In IndexedDatabaseInlines.h + inline bool + SetFromSerialized(const SerializedStructuredCloneReadInfo& aOther); + + JSAutoStructuredCloneBuffer mCloneBuffer; nsTArray mFiles; IDBDatabase* mDatabase; - - // XXX Remove! - JSAutoStructuredCloneBuffer mCloneBuffer; - - // In IndexedDatabaseInlines.h - inline - StructuredCloneReadInfo(); - - // In IndexedDatabaseInlines.h - inline - ~StructuredCloneReadInfo(); - - // In IndexedDatabaseInlines.h - inline StructuredCloneReadInfo& - operator=(StructuredCloneReadInfo&& aOther); - - // In IndexedDatabaseInlines.h - inline - MOZ_IMPLICIT StructuredCloneReadInfo(SerializedStructuredCloneReadInfo&& aOther); }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +struct SerializedStructuredCloneReadInfo +{ + SerializedStructuredCloneReadInfo() + : data(nullptr), dataLength(0) + { } + + bool + operator==(const SerializedStructuredCloneReadInfo& aOther) const + { + return this->data == aOther.data && + this->dataLength == aOther.dataLength; + } + + SerializedStructuredCloneReadInfo& + operator=(const StructuredCloneReadInfo& aOther) + { + data = aOther.mCloneBuffer.data(); + dataLength = aOther.mCloneBuffer.nbytes(); + return *this; + } + + // Make sure to update ipc/SerializationHelpers.h when changing members here! + uint64_t* data; + size_t dataLength; +}; + +struct SerializedStructuredCloneWriteInfo; + +struct StructuredCloneWriteInfo +{ + // In IndexedDatabaseInlines.h + inline StructuredCloneWriteInfo(); + inline StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo); + + bool operator==(const StructuredCloneWriteInfo& aOther) const + { + return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() && + this->mCloneBuffer.data() == aOther.mCloneBuffer.data() && + this->mFiles == aOther.mFiles && + this->mTransaction == aOther.mTransaction && + this->mOffsetToKeyProp == aOther.mOffsetToKeyProp; + } + + // In IndexedDatabaseInlines.h + inline bool + SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther); + + JSAutoStructuredCloneBuffer mCloneBuffer; + nsTArray mFiles; + IDBTransaction* mTransaction; + uint64_t mOffsetToKeyProp; +}; + +struct SerializedStructuredCloneWriteInfo +{ + SerializedStructuredCloneWriteInfo() + : data(nullptr), dataLength(0), offsetToKeyProp(0) + { } + + bool + operator==(const SerializedStructuredCloneWriteInfo& aOther) const + { + return this->data == aOther.data && + this->dataLength == aOther.dataLength && + this->offsetToKeyProp == aOther.offsetToKeyProp; + } + + SerializedStructuredCloneWriteInfo& + operator=(const StructuredCloneWriteInfo& aOther) + { + data = aOther.mCloneBuffer.data(); + dataLength = aOther.mCloneBuffer.nbytes(); + offsetToKeyProp = aOther.mOffsetToKeyProp; + return *this; + } + + // Make sure to update ipc/SerializationHelpers.h when changing members here! + uint64_t* data; + size_t dataLength; + uint64_t offsetToKeyProp; +}; + +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_indexeddatabase_h__ diff --git a/dom/indexedDB/IndexedDatabaseInlines.h b/dom/indexedDB/IndexedDatabaseInlines.h index 1b632217e5ee..b856af67d55d 100644 --- a/dom/indexedDB/IndexedDatabaseInlines.h +++ b/dom/indexedDB/IndexedDatabaseInlines.h @@ -11,55 +11,48 @@ #error Must include IndexedDatabase.h first #endif -#include "FileInfo.h" -#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" -#include "nsIDOMFile.h" -#include "nsIInputStream.h" - -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE inline -StructuredCloneFile::StructuredCloneFile() +StructuredCloneWriteInfo::StructuredCloneWriteInfo() +: mTransaction(nullptr), + mOffsetToKeyProp(0) { - MOZ_COUNT_CTOR(StructuredCloneFile); } inline -StructuredCloneFile::~StructuredCloneFile() +StructuredCloneWriteInfo::StructuredCloneWriteInfo( + StructuredCloneWriteInfo&& aCloneWriteInfo) +: mCloneBuffer(Move(aCloneWriteInfo.mCloneBuffer)) +, mTransaction(aCloneWriteInfo.mTransaction) +, mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp) { - MOZ_COUNT_DTOR(StructuredCloneFile); + mFiles.SwapElements(aCloneWriteInfo.mFiles); + aCloneWriteInfo.mTransaction = nullptr; + aCloneWriteInfo.mOffsetToKeyProp = 0; } inline bool -StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const +StructuredCloneWriteInfo::SetFromSerialized( + const SerializedStructuredCloneWriteInfo& aOther) { - return this->mFile == aOther.mFile && - this->mFileInfo == aOther.mFileInfo; + if (!aOther.dataLength) { + mCloneBuffer.clear(); + } + else if (!mCloneBuffer.copy(aOther.data, aOther.dataLength)) { + return false; + } + + mFiles.Clear(); + mOffsetToKeyProp = aOther.offsetToKeyProp; + return true; } inline StructuredCloneReadInfo::StructuredCloneReadInfo() - : mDatabase(nullptr) +: mDatabase(nullptr) { - MOZ_COUNT_CTOR(StructuredCloneReadInfo); -} - -inline -StructuredCloneReadInfo::StructuredCloneReadInfo( - SerializedStructuredCloneReadInfo&& aCloneReadInfo) - : mData(Move(aCloneReadInfo.data())) - , mDatabase(nullptr) -{ - MOZ_COUNT_CTOR(StructuredCloneReadInfo); -} - -inline -StructuredCloneReadInfo::~StructuredCloneReadInfo() -{ - MOZ_COUNT_DTOR(StructuredCloneReadInfo); } inline StructuredCloneReadInfo& @@ -67,7 +60,6 @@ StructuredCloneReadInfo::operator=(StructuredCloneReadInfo&& aCloneReadInfo) { MOZ_ASSERT(&aCloneReadInfo != this); - mData = Move(aCloneReadInfo.mData); mCloneBuffer = Move(aCloneReadInfo.mCloneBuffer); mFiles.Clear(); mFiles.SwapElements(aCloneReadInfo.mFiles); @@ -76,8 +68,45 @@ StructuredCloneReadInfo::operator=(StructuredCloneReadInfo&& aCloneReadInfo) return *this; } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +inline +bool +StructuredCloneReadInfo::SetFromSerialized( + const SerializedStructuredCloneReadInfo& aOther) +{ + if (aOther.dataLength && + !mCloneBuffer.copy(aOther.data, aOther.dataLength)) { + return false; + } -#endif // IndexedDatabaseInlines_h + mFiles.Clear(); + return true; +} + +inline +void +AppendConditionClause(const nsACString& aColumnName, + const nsACString& aArgName, + bool aLessThan, + bool aEquals, + nsACString& aResult) +{ + aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName + + NS_LITERAL_CSTRING(" "); + + if (aLessThan) { + aResult.Append('<'); + } + else { + aResult.Append('>'); + } + + if (aEquals) { + aResult.Append('='); + } + + aResult += NS_LITERAL_CSTRING(" :") + aArgName; +} + +END_INDEXEDDB_NAMESPACE + +#endif diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index fab61353d767..3f4010d55767 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -16,16 +16,12 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/CondVar.h" #include "mozilla/ContentEvents.h" -#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ErrorEventBinding.h" -#include "mozilla/dom/PBlobChild.h" #include "mozilla/dom/quota/OriginOrPatternString.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/Utilities.h" #include "mozilla/dom/TabContext.h" #include "mozilla/EventDispatcher.h" -#include "mozilla/ipc/BackgroundChild.h" -#include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" #include "mozilla/storage.h" @@ -57,11 +53,12 @@ #define LOW_DISK_SPACE_DATA_FULL "full" #define LOW_DISK_SPACE_DATA_FREE "free" -namespace mozilla { -namespace dom { -namespace indexedDB { +USING_INDEXEDDB_NAMESPACE +using namespace mozilla; +using namespace mozilla::dom; +USING_QUOTA_NAMESPACE -using namespace mozilla::dom::quota; +BEGIN_INDEXEDDB_NAMESPACE class FileManagerInfo { @@ -106,15 +103,14 @@ private: nsTArray > mTemporaryStorageFileManagers; }; -namespace { +END_INDEXEDDB_NAMESPACE -const char kTestingPref[] = "dom.indexedDB.testing"; +namespace { mozilla::StaticRefPtr gDBManager; mozilla::Atomic gInitialized(false); mozilla::Atomic gClosed(false); -mozilla::Atomic gTestingMode(false); class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable { @@ -187,16 +183,6 @@ struct MOZ_STACK_CLASS InvalidateInfo const nsACString& pattern; }; -void -TestingPrefChangedCallback(const char* aPrefName, void* aClosure) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!strcmp(aPrefName, kTestingPref)); - MOZ_ASSERT(!aClosure); - - gTestingMode = Preferences::GetBool(aPrefName); -} - } // anonymous namespace IndexedDatabaseManager::IndexedDatabaseManager() @@ -295,9 +281,6 @@ IndexedDatabaseManager::Init() NS_ENSURE_SUCCESS(rv, rv); } - Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback, - kTestingPref); - return NS_OK; } @@ -310,8 +293,6 @@ IndexedDatabaseManager::Destroy() NS_ERROR("Shutdown more than once?!"); } - Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref); - delete this; } @@ -333,7 +314,7 @@ IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner, nsresult rv = aVisitor.mDOMEvent->GetType(type); NS_ENSURE_SUCCESS(rv, rv); - if (nsDependentString(kErrorEventType) != type) { + if (!type.EqualsLiteral(ERROR_EVT_STR)) { return NS_OK; } @@ -444,9 +425,8 @@ IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx, } nsRefPtr factory; - if (NS_FAILED(IDBFactory::CreateForChromeJS(aCx, - aGlobal, - getter_AddRefs(factory)))) { + if (NS_FAILED(IDBFactory::Create(aCx, aGlobal, nullptr, + getter_AddRefs(factory)))) { return false; } @@ -491,16 +471,6 @@ IndexedDatabaseManager::InLowDiskSpaceMode() } #endif -// static -bool -IndexedDatabaseManager::InTestingMode() -{ - MOZ_ASSERT(gDBManager, - "InTestingMode() called before indexedDB has been initialized!"); - - return gTestingMode; -} - already_AddRefed IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, @@ -656,38 +626,13 @@ IndexedDatabaseManager::BlockAndGetFileReferences( int32_t* aSliceRefCnt, bool* aResult) { - if (NS_WARN_IF(!InTestingMode())) { - return NS_ERROR_UNEXPECTED; - } + nsRefPtr helper = + new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, + aFileId); - if (IsMainProcess()) { - nsRefPtr helper = - new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, - aFileId); - - nsresult rv = - helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, - aSliceRefCnt, aResult); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } else { - ContentChild* contentChild = ContentChild::GetSingleton(); - if (NS_WARN_IF(!contentChild)) { - return NS_ERROR_FAILURE; - } - - if (!contentChild->SendGetFileReferences(aPersistenceType, - nsCString(aOrigin), - nsString(aDatabaseName), - aFileId, - aRefCnt, - aDBRefCnt, - aSliceRefCnt, - aResult)) { - return NS_ERROR_FAILURE; - } - } + nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, + aSliceRefCnt, aResult); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -941,7 +886,3 @@ GetFileReferencesHelper::Run() return NS_OK; } - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 4ce140274ef2..27bad25a8749 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -7,6 +7,8 @@ #ifndef mozilla_dom_indexeddb_indexeddatabasemanager_h__ #define mozilla_dom_indexeddb_indexeddatabasemanager_h__ +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + #include "nsIObserver.h" #include "js/TypeDecls.h" @@ -19,20 +21,16 @@ class nsPIDOMWindow; namespace mozilla { - class EventChainPostVisitor; - namespace dom { - class TabContext; - namespace quota { - class OriginOrPatternString; +} +} +} -} // namespace quota - -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE class FileManager; class FileManagerInfo; @@ -77,9 +75,6 @@ public: } #endif - static bool - InTestingMode(); - already_AddRefed GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, @@ -165,8 +160,6 @@ private: static mozilla::Atomic sLowDiskSpaceMode; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE -#endif // mozilla_dom_indexeddb_indexeddatabasemanager_h__ +#endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */ diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp index 5a847c517791..bc1183f91656 100644 --- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -4,23 +4,19 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/FloatingPoint.h" #include "Key.h" +#include "ReportInternalError.h" -#include -#include "js/Value.h" #include "jsfriendapi.h" -#include "mozilla/Endian.h" -#include "mozilla/FloatingPoint.h" -#include "mozIStorageStatement.h" #include "nsAlgorithm.h" #include "nsJSUtils.h" -#include "ReportInternalError.h" #include "xpcpublic.h" +#include "mozilla/Endian.h" +#include -namespace mozilla { -namespace dom { -namespace indexedDB { +USING_INDEXEDDB_NAMESPACE /* Here's how we encode keys: @@ -266,14 +262,6 @@ Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, #define TWO_BYTE_ADJUST (-0x7F) #define THREE_BYTE_SHIFT 6 -nsresult -Key::EncodeJSVal(JSContext* aCx, - JS::Handle aVal, - uint8_t aTypeOffset) -{ - return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); -} - void Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset) { @@ -326,17 +314,6 @@ Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset) NS_ASSERTION(buffer == mBuffer.EndReading(), "Wrote wrong number of bytes"); } -// static -nsresult -Key::DecodeJSVal(const unsigned char*& aPos, - const unsigned char* aEnd, - JSContext* aCx, - uint8_t aTypeOffset, - JS::MutableHandle aVal) -{ - return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); -} - // static void Key::DecodeString(const unsigned char*& aPos, const unsigned char* aEnd, @@ -451,108 +428,3 @@ Key::DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd) return pun.d; } - -nsresult -Key::BindToStatement(mozIStorageStatement* aStatement, - const nsACString& aParamName) const -{ - nsresult rv = aStatement->BindBlobByName(aParamName, - reinterpret_cast(mBuffer.get()), mBuffer.Length()); - - return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; -} - -nsresult -Key::SetFromStatement(mozIStorageStatement* aStatement, - uint32_t aIndex) -{ - uint8_t* data; - uint32_t dataLength = 0; - - nsresult rv = aStatement->GetBlob(aIndex, &dataLength, &data); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mBuffer.Adopt( - reinterpret_cast(const_cast(data)), dataLength); - - return NS_OK; -} - -nsresult -Key::SetFromJSVal(JSContext* aCx, - JS::Handle aVal) -{ - mBuffer.Truncate(); - - if (aVal.isNull() || aVal.isUndefined()) { - Unset(); - return NS_OK; - } - - nsresult rv = EncodeJSVal(aCx, aVal, 0); - if (NS_FAILED(rv)) { - Unset(); - return rv; - } - TrimBuffer(); - - return NS_OK; -} - -nsresult -Key::ToJSVal(JSContext* aCx, - JS::MutableHandle aVal) const -{ - if (IsUnset()) { - aVal.setUndefined(); - return NS_OK; - } - - const unsigned char* pos = BufferStart(); - nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(pos >= BufferEnd()); - - return NS_OK; -} - -nsresult -Key::ToJSVal(JSContext* aCx, - JS::Heap& aVal) const -{ - JS::Rooted value(aCx); - nsresult rv = ToJSVal(aCx, &value); - if (NS_SUCCEEDED(rv)) { - aVal = value; - } - return rv; -} - -nsresult -Key::AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle aVal) -{ - nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); - if (NS_FAILED(rv)) { - Unset(); - return rv; - } - - return NS_OK; -} - -#ifdef DEBUG - -void -Key::Assert(bool aCondition) const -{ - MOZ_ASSERT(aCondition); -} - -#endif // DEBUG - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index f29443722c46..4ab780c225ed 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -7,91 +7,84 @@ #ifndef mozilla_dom_indexeddb_key_h__ #define mozilla_dom_indexeddb_key_h__ -#include "js/RootingAPI.h" -#include "nsString.h" +#include "mozilla/dom/indexedDB/IndexedDatabase.h" -class mozIStorageStatement; +#include "mozIStorageStatement.h" + +#include "js/Value.h" namespace IPC { - -template struct ParamTraits; - +template struct ParamTraits; } // namespace IPC -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE class Key { friend struct IPC::ParamTraits; - nsCString mBuffer; - public: Key() { Unset(); } - Key& - operator=(const nsAString& aString) + Key& operator=(const nsAString& aString) { SetFromString(aString); return *this; } - Key& - operator=(int64_t aInt) + Key& operator=(int64_t aInt) { SetFromInteger(aInt); return *this; } - bool - operator==(const Key& aOther) const + bool operator==(const Key& aOther) const { - Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); + NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), + "Don't compare unset keys!"); return mBuffer.Equals(aOther.mBuffer); } - bool - operator!=(const Key& aOther) const + bool operator!=(const Key& aOther) const { - Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); + NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), + "Don't compare unset keys!"); return !mBuffer.Equals(aOther.mBuffer); } - bool - operator<(const Key& aOther) const + bool operator<(const Key& aOther) const { - Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); + NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), + "Don't compare unset keys!"); return Compare(mBuffer, aOther.mBuffer) < 0; } - bool - operator>(const Key& aOther) const + bool operator>(const Key& aOther) const { - Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); + NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), + "Don't compare unset keys!"); return Compare(mBuffer, aOther.mBuffer) > 0; } - bool - operator<=(const Key& aOther) const + bool operator<=(const Key& aOther) const { - Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); + NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), + "Don't compare unset keys!"); return Compare(mBuffer, aOther.mBuffer) <= 0; } - bool - operator>=(const Key& aOther) const + bool operator>=(const Key& aOther) const { - Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); + NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), + "Don't compare unset keys!"); return Compare(mBuffer, aOther.mBuffer) >= 0; } @@ -102,114 +95,169 @@ public: mBuffer.SetIsVoid(true); } - bool - IsUnset() const + bool IsUnset() const { return mBuffer.IsVoid(); } - bool - IsFloat() const + bool IsFloat() const { return !IsUnset() && mBuffer.First() == eFloat; } - bool - IsDate() const + bool IsDate() const { return !IsUnset() && mBuffer.First() == eDate; } - bool - IsString() const + bool IsString() const { return !IsUnset() && mBuffer.First() == eString; } - bool - IsArray() const + bool IsArray() const { return !IsUnset() && mBuffer.First() >= eArray; } - double - ToFloat() const + double ToFloat() const { - Assert(IsFloat()); + NS_ASSERTION(IsFloat(), "Why'd you call this?"); const unsigned char* pos = BufferStart(); double res = DecodeNumber(pos, BufferEnd()); - Assert(pos >= BufferEnd()); + NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); return res; } - double - ToDateMsec() const + double ToDateMsec() const { - Assert(IsDate()); + NS_ASSERTION(IsDate(), "Why'd you call this?"); const unsigned char* pos = BufferStart(); double res = DecodeNumber(pos, BufferEnd()); - Assert(pos >= BufferEnd()); + NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); return res; } - void - ToString(nsString& aString) const + void ToString(nsString& aString) const { - Assert(IsString()); + NS_ASSERTION(IsString(), "Why'd you call this?"); const unsigned char* pos = BufferStart(); DecodeString(pos, BufferEnd(), aString); - Assert(pos >= BufferEnd()); + NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); } - void - SetFromString(const nsAString& aString) + void SetFromString(const nsAString& aString) { mBuffer.Truncate(); EncodeString(aString, 0); TrimBuffer(); } - void - SetFromInteger(int64_t aInt) + void SetFromInteger(int64_t aInt) { mBuffer.Truncate(); EncodeNumber(double(aInt), eFloat); TrimBuffer(); } - nsresult - SetFromJSVal(JSContext* aCx, JS::Handle aVal); + nsresult SetFromJSVal(JSContext* aCx, + JS::Handle aVal) + { + mBuffer.Truncate(); - nsresult - ToJSVal(JSContext* aCx, JS::MutableHandle aVal) const; + if (aVal.isNull() || aVal.isUndefined()) { + Unset(); + return NS_OK; + } - nsresult - ToJSVal(JSContext* aCx, JS::Heap& aVal) const; + nsresult rv = EncodeJSVal(aCx, aVal, 0); + if (NS_FAILED(rv)) { + Unset(); + return rv; + } + TrimBuffer(); - nsresult - AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle aVal); + return NS_OK; + } - void - FinishArray() + nsresult ToJSVal(JSContext* aCx, + JS::MutableHandle aVal) const + { + if (IsUnset()) { + aVal.setUndefined(); + return NS_OK; + } + + const unsigned char* pos = BufferStart(); + nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(pos >= BufferEnd(), + "Didn't consume whole buffer"); + + return NS_OK; + } + + nsresult ToJSVal(JSContext* aCx, + JS::Heap& aVal) const + { + JS::Rooted value(aCx); + nsresult rv = ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + aVal = value; + } + return rv; + } + + nsresult AppendItem(JSContext* aCx, + bool aFirstOfArray, + JS::Handle aVal) + { + nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); + if (NS_FAILED(rv)) { + Unset(); + return rv; + } + + return NS_OK; + } + + void FinishArray() { TrimBuffer(); } - const nsCString& - GetBuffer() const + const nsCString& GetBuffer() const { return mBuffer; } - nsresult - BindToStatement(mozIStorageStatement* aStatement, - const nsACString& aParamName) const; + nsresult BindToStatement(mozIStorageStatement* aStatement, + const nsACString& aParamName) const + { + nsresult rv = aStatement->BindBlobByName(aParamName, + reinterpret_cast(mBuffer.get()), mBuffer.Length()); - nsresult - SetFromStatement(mozIStorageStatement* aStatement, uint32_t aIndex); + return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - static int16_t - CompareKeys(Key& aFirst, Key& aSecond) + nsresult SetFromStatement(mozIStorageStatement* aStatement, + uint32_t aIndex) + { + uint8_t* data; + uint32_t dataLength = 0; + + nsresult rv = aStatement->GetBlob(aIndex, &dataLength, &data); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mBuffer.Adopt( + reinterpret_cast(const_cast(data)), dataLength); + + return NS_OK; + } + + static + int16_t CompareKeys(Key& aFirst, Key& aSecond) { int32_t result = Compare(aFirst.mBuffer, aSecond.mBuffer); @@ -225,14 +273,12 @@ public: } private: - const unsigned char* - BufferStart() const + const unsigned char* BufferStart() const { return reinterpret_cast(mBuffer.BeginReading()); } - const unsigned char* - BufferEnd() const + const unsigned char* BufferEnd() const { return reinterpret_cast(mBuffer.EndReading()); } @@ -248,8 +294,7 @@ private: // Encoding helper. Trims trailing zeros off of mBuffer as a post-processing // step. - void - TrimBuffer() + void TrimBuffer() { const char* end = mBuffer.EndReading() - 1; while (!*end) { @@ -260,57 +305,41 @@ private: } // Encoding functions. These append the encoded value to the end of mBuffer - nsresult - EncodeJSVal(JSContext* aCx, JS::Handle aVal, uint8_t aTypeOffset); - - void - EncodeString(const nsAString& aString, uint8_t aTypeOffset); - - void - EncodeNumber(double aFloat, uint8_t aType); + inline nsresult EncodeJSVal(JSContext* aCx, JS::Handle aVal, + uint8_t aTypeOffset) + { + return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); + } + void EncodeString(const nsAString& aString, uint8_t aTypeOffset); + void EncodeNumber(double aFloat, uint8_t aType); // Decoding functions. aPos points into mBuffer and is adjusted to point // past the consumed value. - static nsresult - DecodeJSVal(const unsigned char*& aPos, - const unsigned char* aEnd, - JSContext* aCx, - uint8_t aTypeOffset, - JS::MutableHandle aVal); + static inline nsresult DecodeJSVal(const unsigned char*& aPos, + const unsigned char* aEnd, JSContext* aCx, + uint8_t aTypeOffset, JS::MutableHandle aVal) + { + return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); + } - static void - DecodeString(const unsigned char*& aPos, - const unsigned char* aEnd, - nsString& aString); + static void DecodeString(const unsigned char*& aPos, + const unsigned char* aEnd, + nsString& aString); + static double DecodeNumber(const unsigned char*& aPos, + const unsigned char* aEnd); - static double - DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd); + nsCString mBuffer; - nsresult - EncodeJSValInternal(JSContext* aCx, - JS::Handle aVal, - uint8_t aTypeOffset, - uint16_t aRecursionDepth); +private: + nsresult EncodeJSValInternal(JSContext* aCx, JS::Handle aVal, + uint8_t aTypeOffset, uint16_t aRecursionDepth); - static nsresult - DecodeJSValInternal(const unsigned char*& aPos, - const unsigned char* aEnd, - JSContext* aCx, - uint8_t aTypeOffset, - JS::MutableHandle aVal, - uint16_t aRecursionDepth); - - void - Assert(bool aCondition) const -#ifdef DEBUG - ; -#else - { } -#endif + static nsresult DecodeJSValInternal(const unsigned char*& aPos, + const unsigned char* aEnd, + JSContext* aCx, uint8_t aTypeOffset, + JS::MutableHandle aVal, uint16_t aRecursionDepth); }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE -#endif // mozilla_dom_indexeddb_key_h__ +#endif /* mozilla_dom_indexeddb_key_h__ */ diff --git a/dom/indexedDB/KeyPath.cpp b/dom/indexedDB/KeyPath.cpp index 9f440746372c..e3ad5b3d2891 100644 --- a/dom/indexedDB/KeyPath.cpp +++ b/dom/indexedDB/KeyPath.cpp @@ -15,9 +15,7 @@ #include "mozilla/dom/BindingDeclarations.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +USING_INDEXEDDB_NAMESPACE namespace { @@ -180,9 +178,8 @@ GetJSValFromKeyPathString(JSContext* aCx, obj = dummy; } else { - JS::Rooted dummy(aCx, - JS_NewObject(aCx, IDBObjectStore::DummyPropClass(), JS::NullPtr(), - JS::NullPtr())); + JS::Rooted dummy(aCx, JS_NewObject(aCx, &IDBObjectStore::sDummyPropJSClass, + JS::NullPtr(), JS::NullPtr())); if (!dummy) { IDB_REPORT_INTERNAL_ERR(); rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -572,7 +569,3 @@ KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const // Everything else is ok. return true; } - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/KeyPath.h b/dom/indexedDB/KeyPath.h index abcd22c0022e..bee0c2cad0e8 100644 --- a/dom/indexedDB/KeyPath.h +++ b/dom/indexedDB/KeyPath.h @@ -7,28 +7,16 @@ #ifndef mozilla_dom_indexeddb_keypath_h__ #define mozilla_dom_indexeddb_keypath_h__ +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + #include "mozilla/dom/BindingDeclarations.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE -class IndexMetadata; class Key; -class ObjectStoreMetadata; class KeyPath { - // This private constructor is only to be used by IPDL-generated classes. - friend class IndexMetadata; - friend class ObjectStoreMetadata; - - KeyPath() - : mType(NONEXISTENT) - { - MOZ_COUNT_CTOR(KeyPath); - } - public: enum KeyPathType { NONEXISTENT, @@ -117,8 +105,6 @@ public: nsTArray mStrings; }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE -#endif // mozilla_dom_indexeddb_keypath_h__ +#endif /* mozilla_dom_indexeddb_keypath_h__ */ diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp new file mode 100644 index 000000000000..1a07825d44e5 --- /dev/null +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -0,0 +1,2865 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/DebugOnly.h" + +#include "OpenDatabaseHelper.h" + +#include "nsIBFCacheEntry.h" +#include "nsIFile.h" + +#include +#include "mozilla/dom/quota/AcquireListener.h" +#include "mozilla/dom/quota/OriginOrPatternString.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/storage.h" +#include "nsEscape.h" +#include "nsNetUtil.h" +#include "nsThreadUtils.h" +#include "snappy/snappy.h" + +#include "Client.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IndexedDatabaseManager.h" +#include "ProfilerHelpers.h" +#include "ReportInternalError.h" + +using namespace mozilla; +using namespace mozilla::dom; +USING_INDEXEDDB_NAMESPACE +USING_QUOTA_NAMESPACE + +namespace { + +// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major +// schema version. +static_assert(JS_STRUCTURED_CLONE_VERSION == 5, + "Need to update the major schema version."); + +// Major schema version. Bump for almost everything. +const uint32_t kMajorSchemaVersion = 17; + +// Minor schema version. Should almost always be 0 (maybe bump on release +// branches if we have to). +const uint32_t kMinorSchemaVersion = 0; + +// The schema version we store in the SQLite database is a (signed) 32-bit +// integer. The major version is left-shifted 4 bits so the max value is +// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF. +static_assert(kMajorSchemaVersion <= 0xFFFFFFF, + "Major version needs to fit in 28 bits."); +static_assert(kMinorSchemaVersion <= 0xF, + "Minor version needs to fit in 4 bits."); + +inline +int32_t +MakeSchemaVersion(uint32_t aMajorSchemaVersion, + uint32_t aMinorSchemaVersion) +{ + return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion); +} + +const int32_t kSQLiteSchemaVersion = int32_t((kMajorSchemaVersion << 4) + + kMinorSchemaVersion); + +const uint32_t kGoldenRatioU32 = 0x9E3779B9U; + +inline +uint32_t +RotateBitsLeft32(uint32_t value, uint8_t bits) +{ + MOZ_ASSERT(bits < 32); + return (value << bits) | (value >> (32 - bits)); +} + +inline +uint32_t +HashName(const nsAString& aName) +{ + const char16_t* str = aName.BeginReading(); + size_t length = aName.Length(); + + uint32_t hash = 0; + for (size_t i = 0; i < length; i++) { + hash = kGoldenRatioU32 * (RotateBitsLeft32(hash, 5) ^ str[i]); + } + + return hash; +} + +nsresult +GetDatabaseFilename(const nsAString& aName, + nsAString& aDatabaseFilename) +{ + aDatabaseFilename.AppendInt(HashName(aName)); + + nsCString escapedName; + if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { + NS_WARNING("Can't escape database name!"); + return NS_ERROR_UNEXPECTED; + } + + const char* forwardIter = escapedName.BeginReading(); + const char* backwardIter = escapedName.EndReading() - 1; + + nsCString substring; + while (forwardIter <= backwardIter && substring.Length() < 21) { + if (substring.Length() % 2) { + substring.Append(*backwardIter--); + } + else { + substring.Append(*forwardIter++); + } + } + + aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring)); + + return NS_OK; +} + +nsresult +CreateFileTables(mozIStorageConnection* aDBConn) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "CreateFileTables", + js::ProfileEntry::Category::STORAGE); + + // Table `file` + nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE file (" + "id INTEGER PRIMARY KEY, " + "refcount INTEGER NOT NULL" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER file_update_trigger " + "AFTER UPDATE ON file " + "FOR EACH ROW WHEN NEW.refcount = 0 " + "BEGIN " + "DELETE FROM file WHERE id = OLD.id; " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +CreateTables(mozIStorageConnection* aDBConn) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aDBConn, "Passing a null database connection!"); + + PROFILER_LABEL("OpenDatabaseHelper", "CreateTables", + js::ProfileEntry::Category::STORAGE); + + // Table `database` + nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `object_store` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store (" + "id INTEGER PRIMARY KEY, " + "auto_increment INTEGER NOT NULL DEFAULT 0, " + "name TEXT NOT NULL, " + "key_path TEXT, " + "UNIQUE (name)" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `object_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value BLOB DEFAULT NULL, " + "file_ids TEXT, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `index` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Need this to make cascading deletes from object_data and object_store fast. + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Table `unique_index_data` + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Need this to make cascading deletes from object_data and object_store fast. + rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CreateFileTables(aDBConn); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom4To5", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + // All we changed is the type of the version column, so lets try to + // convert that to an integer, and if we fail, set it to 0. + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, version, dataVersion " + "FROM database" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString name; + int32_t intVersion; + int64_t dataVersion; + + { + mozStorageStatementScoper scoper(stmt); + + bool hasResults; + rv = stmt->ExecuteStep(&hasResults); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(hasResults, NS_ERROR_FAILURE); + + nsString version; + rv = stmt->GetString(1, version); + NS_ENSURE_SUCCESS(rv, rv); + + intVersion = version.ToInteger(&rv); + if (NS_FAILED(rv)) { + intVersion = 0; + } + + rv = stmt->GetString(0, name); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->GetInt64(2, &dataVersion); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE database" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0, " + "dataVersion INTEGER NOT NULL" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database (name, version, dataVersion) " + "VALUES (:name, :version, :dataVersion)" + ), getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + { + mozStorageStatementScoper scoper(stmt); + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), name); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), intVersion); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("dataVersion"), dataVersion); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = aConnection->SetSchemaVersion(5); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom5To6", + js::ProfileEntry::Category::STORAGE); + + // First, drop all the indexes we're no longer going to use. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX key_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX ai_key_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX value_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX ai_value_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Now, reorder the columns of object_data to put the blob data last. We do + // this by copying into a temporary table, dropping the original, then copying + // back into a newly created table. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "key_value, " + "data " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, key_value, data " + "FROM object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value DEFAULT NULL, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data " + "SELECT id, object_store_id, key_value, data " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // We need to add a unique constraint to our ai_object_data table. Copy all + // the data out of it using a temporary table as before. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "data " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, data " + "FROM ai_object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_object_data (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "object_store_id INTEGER NOT NULL, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, id), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO ai_object_data " + "SELECT id, object_store_id, data " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Fix up the index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "object_data_key NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT OR IGNORE INTO index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Fix up the unique_index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "object_data_key NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Fix up the ai_index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "ai_object_data_id " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, ai_object_data_id " + "FROM ai_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT OR IGNORE INTO ai_index_data " + "SELECT index_id, value, ai_object_data_id " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_index_data_ai_object_data_id_index " + "ON ai_index_data (ai_object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + // Fix up the ai_unique_index_data table. We're reordering the columns as well + // as changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "ai_object_data_id " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, ai_object_data_id " + "FROM ai_unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "UNIQUE (index_id, value), " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO ai_unique_index_data " + "SELECT index_id, value, ai_object_data_id " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_unique_index_data_ai_object_data_id_index " + "ON ai_unique_index_data (ai_object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(6); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom6To7", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "name, " + "key_path, " + "auto_increment" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, name, key_path, auto_increment " + "FROM object_store;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store (" + "id INTEGER PRIMARY KEY, " + "auto_increment INTEGER NOT NULL DEFAULT 0, " + "name TEXT NOT NULL, " + "key_path TEXT, " + "UNIQUE (name)" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store " + "SELECT id, auto_increment, name, nullif(key_path, '') " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(7); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom7To8", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "object_store_autoincrement" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, object_store_autoincrement " + "FROM object_store_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "object_store_autoincrement INTERGER NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, 0, object_store_autoincrement " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(8); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +class CompressDataBlobsFunction MOZ_FINAL : public mozIStorageFunction +{ + ~CompressDataBlobsFunction() {} + +public: + NS_DECL_ISUPPORTS + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) + { + PROFILER_LABEL("CompressDataBlobsFunction", "OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + NS_ENSURE_SUCCESS(rv, rv); + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + NS_ENSURE_SUCCESS(rv, rv); + + if (type != mozIStorageStatement::VALUE_TYPE_BLOB) { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const uint8_t* uncompressed; + uint32_t uncompressedLength; + rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed); + NS_ENSURE_SUCCESS(rv, rv); + + size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); + char* compressed = (char*)moz_malloc(compressedLength); + NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY); + + snappy::RawCompress(reinterpret_cast(uncompressed), + uncompressedLength, compressed, &compressedLength); + + std::pair data((uint8_t*)compressed, + int(compressedLength)); + // The variant takes ownership of | compressed |. + nsCOMPtr result = new mozilla::storage::AdoptedBlobVariant(data); + + result.forget(aResult); + return NS_OK; + } +}; + +NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction) + +nsresult +UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom8To9_0", + js::ProfileEntry::Category::STORAGE); + + // We no longer use the dataVersion column. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE database SET dataVersion = 0;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr compressor = new CompressDataBlobsFunction(); + + NS_NAMED_LITERAL_CSTRING(compressorName, "compress"); + + rv = aConnection->CreateFunction(compressorName, 1, compressor); + NS_ENSURE_SUCCESS(rv, rv); + + // Turn off foreign key constraints before we do anything here. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_data SET data = compress(data);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE ai_object_data SET data = compress(data);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->RemoveFunction(compressorName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom9_0To10_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_data ADD COLUMN file_ids TEXT;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CreateFileTables(aConnection); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom10_0To11_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "multientry" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, multientry " + "FROM object_store_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, multientry " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TRIGGER object_data_insert_trigger;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " + "SELECT object_store_id, id, data, file_ids " + "FROM ai_object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) " + "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id " + "FROM ai_index_data " + "INNER JOIN object_store_index ON " + "object_store_index.id = ai_index_data.index_id " + "INNER JOIN object_data ON " + "object_data.object_store_id = object_store_index.object_store_id AND " + "object_data.key_value = ai_index_data.ai_object_data_id;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) " + "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id " + "FROM ai_unique_index_data " + "INNER JOIN object_store_index ON " + "object_store_index.id = ai_unique_index_data.index_id " + "INNER JOIN object_data ON " + "object_data.object_store_id = object_store_index.object_store_id AND " + "object_data.key_value = ai_unique_index_data.ai_object_data_id;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_store " + "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 " + "WHERE auto_increment;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +class EncodeKeysFunction MOZ_FINAL : public mozIStorageFunction +{ + ~EncodeKeysFunction() {} + +public: + NS_DECL_ISUPPORTS + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) + { + PROFILER_LABEL("EncodeKeysFunction", "OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + NS_ENSURE_SUCCESS(rv, rv); + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + NS_ENSURE_SUCCESS(rv, rv); + + Key key; + if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) { + int64_t intKey; + aArguments->GetInt64(0, &intKey); + key.SetFromInteger(intKey); + } + else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) { + nsString stringKey; + aArguments->GetString(0, stringKey); + key.SetFromString(stringKey); + } + else { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const nsCString& buffer = key.GetBuffer(); + + std::pair data(static_cast(buffer.get()), + int(buffer.Length())); + + nsCOMPtr result = new mozilla::storage::BlobVariant(data); + + result.forget(aResult); + return NS_OK; + } +}; + +NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction) + +nsresult +UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom11_0To12_0", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(encoderName, "encode"); + + nsCOMPtr encoder = new EncodeKeysFunction(); + + nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "key_value, " + "data, " + "file_ids " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, encode(key_value), data, file_ids " + "FROM object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value BLOB DEFAULT NULL, " + "file_ids TEXT, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data " + "SELECT id, object_store_id, key_value, file_ids, data " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, encode(value), encode(object_data_key), object_data_id " + "FROM index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, encode(value), encode(object_data_key), object_data_id " + "FROM unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE unique_index_data;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->RemoveFunction(encoderName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection, + bool* aVacuumNeeded) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom12_0To13_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) + int32_t defaultPageSize; + rv = aConnection->GetDefaultPageSize(&defaultPageSize); + NS_ENSURE_SUCCESS(rv, rv); + + // Enable auto_vacuum mode and update the page size to the platform default. + nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = "); + upgradeQuery.AppendInt(defaultPageSize); + + rv = aConnection->ExecuteSimpleSQL(upgradeQuery); + NS_ENSURE_SUCCESS(rv, rv); + + *aVacuumNeeded = true; +#endif + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection) +{ + // The only change between 13 and 14 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection) +{ + // The only change between 14 and 15 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection) +{ + // The only change between 15 and 16 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection) +{ + // The only change between 16 and 17 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +class VersionChangeEventsRunnable; + +class SetVersionHelper : public AsyncConnectionHelper, + public IDBTransactionListener, + public AcquireListener +{ + friend class VersionChangeEventsRunnable; + +public: + SetVersionHelper(IDBTransaction* aTransaction, + IDBOpenDBRequest* aRequest, + OpenDatabaseHelper* aHelper, + uint64_t aRequestedVersion, + uint64_t aCurrentVersion) + : AsyncConnectionHelper(aTransaction, aRequest), + mOpenRequest(aRequest), mOpenHelper(aHelper), + mRequestedVersion(aRequestedVersion), + mCurrentVersion(aCurrentVersion) + { + mTransaction->SetTransactionListener(this); + } + + NS_DECL_ISUPPORTS_INHERITED + + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual nsresult + OnExclusiveAccessAcquired() MOZ_OVERRIDE; + +protected: + virtual ~SetVersionHelper() {} + + virtual nsresult Init() MOZ_OVERRIDE; + + virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) + MOZ_OVERRIDE; + + // SetVersionHelper never fires an error event at the request. It hands that + // responsibility back to the OpenDatabaseHelper + virtual void OnError() MOZ_OVERRIDE + { } + + // Need an upgradeneeded event here. + virtual already_AddRefed CreateSuccessEvent( + mozilla::dom::EventTarget* aOwner) MOZ_OVERRIDE; + + virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) + MOZ_OVERRIDE; + virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) + MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE + { + return Success_NotSent; + } + + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE + { + MOZ_CRASH("Should never get here!"); + } + + uint64_t RequestedVersion() const + { + return mRequestedVersion; + } + +private: + // In-params + nsRefPtr mOpenRequest; + nsRefPtr mOpenHelper; + uint64_t mRequestedVersion; + uint64_t mCurrentVersion; +}; + +class DeleteDatabaseHelper : public AsyncConnectionHelper, + public AcquireListener +{ + friend class VersionChangeEventsRunnable; +public: + DeleteDatabaseHelper(IDBOpenDBRequest* aRequest, + OpenDatabaseHelper* aHelper, + uint64_t aCurrentVersion, + const nsAString& aName, + const nsACString& aGroup, + const nsACString& aASCIIOrigin, + PersistenceType aPersistenceType) + : AsyncConnectionHelper(static_cast(nullptr), aRequest), + mOpenHelper(aHelper), mOpenRequest(aRequest), + mCurrentVersion(aCurrentVersion), mName(aName), + mGroup(aGroup), mASCIIOrigin(aASCIIOrigin), + mPersistenceType(aPersistenceType) + { } + + NS_DECL_ISUPPORTS_INHERITED + + nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal); + + void ReleaseMainThreadObjects() + { + mOpenHelper = nullptr; + mOpenRequest = nullptr; + + AsyncConnectionHelper::ReleaseMainThreadObjects(); + } + + virtual nsresult + OnExclusiveAccessAcquired() MOZ_OVERRIDE; + +protected: + virtual ~DeleteDatabaseHelper() {} + + nsresult DoDatabaseWork(mozIStorageConnection* aConnection); + nsresult Init(); + + // DeleteDatabaseHelper never fires events at the request. It hands that + // responsibility back to the OpenDatabaseHelper + void OnError() + { + mOpenHelper->NotifyDeleteFinished(); + } + + nsresult OnSuccess() + { + return mOpenHelper->NotifyDeleteFinished(); + } + + uint64_t RequestedVersion() const + { + return 0; + } + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE + { + return Success_NotSent; + } + + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE + { + MOZ_CRASH("Should never get here!"); + } + +private: + // In-params + nsRefPtr mOpenHelper; + nsRefPtr mOpenRequest; + uint64_t mCurrentVersion; + nsString mName; + nsCString mGroup; + nsCString mASCIIOrigin; + PersistenceType mPersistenceType; +}; + +// Responsible for firing "versionchange" events at all live and non-closed +// databases, and for firing a "blocked" event at the requesting database if any +// databases fail to close. +class VersionChangeEventsRunnable : public nsRunnable +{ +public: + VersionChangeEventsRunnable( + IDBDatabase* aRequestingDatabase, + IDBOpenDBRequest* aRequest, + nsTArray >& aWaitingDatabases, + int64_t aOldVersion, + int64_t aNewVersion) + : mRequestingDatabase(aRequestingDatabase), + mRequest(aRequest), + mOldVersion(aOldVersion), + mNewVersion(aNewVersion) + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aRequestingDatabase, "Null pointer!"); + NS_ASSERTION(aRequest, "Null pointer!"); + + mWaitingDatabases.SwapElements(aWaitingDatabases); + } + + NS_IMETHOD Run() + { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "VersionChangeEventsRunnable::Run", + js::ProfileEntry::Category::STORAGE); + + // Fire version change events at all of the databases that are not already + // closed. Also kick bfcached documents out of bfcache. + uint32_t count = mWaitingDatabases.Length(); + for (uint32_t index = 0; index < count; index++) { + IDBDatabase* database = + IDBDatabase::FromStorage(mWaitingDatabases[index]); + NS_ASSERTION(database, "This shouldn't be null!"); + + if (database->IsClosed()) { + continue; + } + + // First check if the document the IDBDatabase is part of is bfcached. + nsCOMPtr ownerDoc = database->GetOwnerDocument(); + nsIBFCacheEntry* bfCacheEntry; + if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) { + bfCacheEntry->RemoveFromBFCacheSync(); + NS_ASSERTION(database->IsClosed(), + "Kicking doc out of bfcache should have closed database"); + continue; + } + + // Next check if it's in the process of being bfcached. + nsPIDOMWindow* owner = database->GetOwner(); + if (owner && owner->IsFrozen()) { + // We can't kick the document out of the bfcache because it's not yet + // fully in the bfcache. Instead we'll abort everything for the window + // and mark it as not-bfcacheable. + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Huh?"); + quotaManager->AbortCloseStoragesForWindow(owner); + + NS_ASSERTION(database->IsClosed(), + "AbortCloseStoragesForWindow should have closed database"); + if (ownerDoc) { + ownerDoc->DisallowBFCaching(); + } + continue; + } + + // Otherwise fire a versionchange event. + nsRefPtr event = + IDBVersionChangeEvent::Create(database, mOldVersion, mNewVersion); + NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); + + bool dummy; + database->DispatchEvent(event, &dummy); + } + + // Now check to see if any didn't close. If there are some running still + // then fire the blocked event. + for (uint32_t index = 0; index < count; index++) { + if (!mWaitingDatabases[index]->IsClosed()) { + nsRefPtr event = + IDBVersionChangeEvent::CreateBlocked(mRequest, + mOldVersion, mNewVersion); + NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); + + bool dummy; + mRequest->DispatchEvent(event, &dummy); + + break; + } + } + + return NS_OK; + } + + template + static + void QueueVersionChange(nsTArray >& aDatabases, + void* aClosure); +private: + nsRefPtr mRequestingDatabase; + nsRefPtr mRequest; + nsTArray > mWaitingDatabases; + int64_t mOldVersion; + int64_t mNewVersion; +}; + +} // anonymous namespace + +NS_IMPL_ISUPPORTS(OpenDatabaseHelper, nsIRunnable) + +nsresult +OpenDatabaseHelper::Init() +{ + QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, Client::IDB, + mName, mDatabaseId); + MOZ_ASSERT(!mDatabaseId.IsEmpty()); + + return NS_OK; +} + +nsresult +OpenDatabaseHelper::WaitForOpenAllowed() +{ + NS_ASSERTION(mState == eCreated, "We've already been dispatched?"); + NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!"); + + mState = eOpenPending; + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + return quotaManager-> + WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mASCIIOrigin), + Nullable(mPersistenceType), mDatabaseId, + this); +} + +nsresult +OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget) +{ + NS_ASSERTION(mState == eCreated || mState == eOpenPending, + "We've already been dispatched?"); + + mState = eDBWork; + + return aTarget->Dispatch(this, NS_DISPATCH_NORMAL); +} + +nsresult +OpenDatabaseHelper::DispatchToIOThread() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + return Dispatch(quotaManager->IOThread()); +} + +nsresult +OpenDatabaseHelper::RunImmediately() +{ + NS_ASSERTION(mState == eCreated || mState == eOpenPending, + "We've already been dispatched?"); + NS_ASSERTION(NS_FAILED(mResultCode), + "Should only be short-circuiting if we failed!"); + NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!"); + + mState = eFiringEvents; + + return this->Run(); +} + +nsresult +OpenDatabaseHelper::DoDatabaseWork() +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + mState = eFiringEvents; // In case we fail somewhere along the line. + + if (QuotaManager::IsShuttingDown()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + NS_ASSERTION(mOpenDBRequest, "This should never be null!"); + + // This will be null for non-window contexts. + nsPIDOMWindow* window = mOpenDBRequest->GetOwner(); + + AutoEnterWindow autoWindow(window); + + nsCOMPtr dbDirectory; + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + nsresult rv = + quotaManager->EnsureOriginIsInitialized(mPersistenceType, mGroup, + mASCIIOrigin, mTrackingQuota, + getter_AddRefs(dbDirectory)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + bool exists; + rv = dbDirectory->Exists(&exists); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (!exists) { + rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } +#ifdef DEBUG + else { + bool isDirectory; + NS_ASSERTION(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)) && + isDirectory, "Should have caught this earlier!"); + } +#endif + + nsAutoString filename; + rv = GetDatabaseFilename(mName, filename); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr dbFile; + rv = dbDirectory->Clone(getter_AddRefs(dbFile)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbFile->GetPath(mDatabaseFilePath); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr fmDirectory; + rv = dbDirectory->Clone(getter_AddRefs(fmDirectory)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = fmDirectory->Append(filename); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr connection; + rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mPersistenceType, + mGroup, mASCIIOrigin, + getter_AddRefs(connection)); + if (NS_FAILED(rv) && + NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { + IDB_REPORT_INTERNAL_ERR(); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, + &mCurrentVersion, mObjectStores); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (mForDeletion) { + mState = eDeletePending; + return NS_OK; + } + + for (uint32_t i = 0; i < mObjectStores.Length(); i++) { + nsRefPtr& objectStoreInfo = mObjectStores[i]; + for (uint32_t j = 0; j < objectStoreInfo->indexes.Length(); j++) { + IndexInfo& indexInfo = objectStoreInfo->indexes[j]; + mLastIndexId = std::max(indexInfo.id, mLastIndexId); + } + mLastObjectStoreId = std::max(objectStoreInfo->id, mLastObjectStoreId); + } + + // See if we need to do a VERSION_CHANGE transaction + + // Optional version semantics. + if (!mRequestedVersion) { + // If the requested version was not specified and the database was created, + // treat it as if version 1 were requested. + if (mCurrentVersion == 0) { + mRequestedVersion = 1; + } + else { + // Otherwise, treat it as if the current version were requested. + mRequestedVersion = mCurrentVersion; + } + } + + if (mCurrentVersion > mRequestedVersion) { + return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR; + } + + if (mCurrentVersion != mRequestedVersion) { + mState = eSetVersionPending; + } + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + NS_ASSERTION(mgr, "This should never be null!"); + + nsRefPtr fileManager = + mgr->GetFileManager(mPersistenceType, mASCIIOrigin, mName); + if (!fileManager) { + fileManager = new FileManager(mPersistenceType, mGroup, mASCIIOrigin, + mPrivilege, mName); + + rv = fileManager->Init(fmDirectory, connection); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mgr->AddFileManager(fileManager); + } + + mFileManager = fileManager.forget(); + + return NS_OK; +} + +// static +nsresult +OpenDatabaseHelper::CreateDatabaseConnection( + nsIFile* aDBFile, + nsIFile* aFMDirectory, + const nsAString& aName, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + mozIStorageConnection** aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("OpenDatabaseHelper", "CreateDatabaseConnection", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + bool exists; + + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { + rv = aDBFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) { + NS_WARNING("Refusing to create database because disk space is low!"); + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + } + + nsCOMPtr dbFileUrl = + IDBFactory::GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin); + NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE); + + nsCOMPtr ss = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); + + nsCOMPtr connection; + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); + if (rv == NS_ERROR_FILE_CORRUPTED) { + // If we're just opening the database during origin initialization, then + // we don't want to erase any files. The failure here will fail origin + // initialization too. + if (aName.IsVoid()) { + return rv; + } + + // Nuke the database file. The web services can recreate their data. + rv = aDBFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aFMDirectory->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + bool isDirectory; + rv = aFMDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = aFMDirectory->Remove(true); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = IDBFactory::SetDefaultPragmas(connection); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem")); + NS_ENSURE_SUCCESS(rv, rv); + + // Check to make sure that the database schema is correct. + int32_t schemaVersion; + rv = connection->GetSchemaVersion(&schemaVersion); + NS_ENSURE_SUCCESS(rv, rv); + + // Unknown schema will fail origin initialization too + if (!schemaVersion && aName.IsVoid()) { + IDB_WARNING("Unable to open IndexedDB database, schema is not set!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (schemaVersion > kSQLiteSchemaVersion) { + IDB_WARNING("Unable to open IndexedDB database, schema is too high!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + bool vacuumNeeded = false; + + if (schemaVersion != kSQLiteSchemaVersion) { +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) + if (!schemaVersion) { + // Have to do this before opening a transaction. + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Turn on auto_vacuum mode to reclaim disk space on mobile devices. + "PRAGMA auto_vacuum = FULL; " + )); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, + // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. + rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + NS_ENSURE_SUCCESS(rv, rv); + } +#endif + + mozStorageTransaction transaction(connection, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + + if (!schemaVersion) { + // Brand new file, initialize our tables. + rv = CreateTables(connection); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) && + schemaVersion == kSQLiteSchemaVersion, + "CreateTables set a bad schema version!"); + + nsCOMPtr stmt; + nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database (name) " + "VALUES (:name)" + ), getter_AddRefs(stmt)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->Execute(); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + else { + // This logic needs to change next time we change the schema! + static_assert(kSQLiteSchemaVersion == int32_t((17 << 4) + 0), + "Need upgrade code from schema version increase."); + + while (schemaVersion != kSQLiteSchemaVersion) { + if (schemaVersion == 4) { + rv = UpgradeSchemaFrom4To5(connection); + } + else if (schemaVersion == 5) { + rv = UpgradeSchemaFrom5To6(connection); + } + else if (schemaVersion == 6) { + rv = UpgradeSchemaFrom6To7(connection); + } + else if (schemaVersion == 7) { + rv = UpgradeSchemaFrom7To8(connection); + } + else if (schemaVersion == 8) { + rv = UpgradeSchemaFrom8To9_0(connection); + vacuumNeeded = true; + } + else if (schemaVersion == MakeSchemaVersion(9, 0)) { + rv = UpgradeSchemaFrom9_0To10_0(connection); + } + else if (schemaVersion == MakeSchemaVersion(10, 0)) { + rv = UpgradeSchemaFrom10_0To11_0(connection); + } + else if (schemaVersion == MakeSchemaVersion(11, 0)) { + rv = UpgradeSchemaFrom11_0To12_0(connection); + } + else if (schemaVersion == MakeSchemaVersion(12, 0)) { + rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded); + } + else if (schemaVersion == MakeSchemaVersion(13, 0)) { + rv = UpgradeSchemaFrom13_0To14_0(connection); + } + else if (schemaVersion == MakeSchemaVersion(14, 0)) { + rv = UpgradeSchemaFrom14_0To15_0(connection); + } + else if (schemaVersion == MakeSchemaVersion(15, 0)) { + rv = UpgradeSchemaFrom15_0To16_0(connection); + } + else if (schemaVersion == MakeSchemaVersion(16, 0)) { + rv = UpgradeSchemaFrom16_0To17_0(connection); + } + else { + NS_WARNING("Unable to open IndexedDB database, no upgrade path is " + "available!"); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->GetSchemaVersion(&schemaVersion); + NS_ENSURE_SUCCESS(rv, rv); + } + + NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!"); + } + + rv = transaction.Commit(); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, + // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. + rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + NS_ENSURE_SUCCESS(rv, rv); + } + + if (vacuumNeeded) { + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;")); + NS_ENSURE_SUCCESS(rv, rv); + } + + connection.forget(aConnection); + return NS_OK; +} + +nsresult +OpenDatabaseHelper::StartSetVersion() +{ + NS_ASSERTION(mState == eSetVersionPending, "Why are we here?"); + + // In case we fail, fire error events + mState = eFiringEvents; + + nsresult rv = EnsureSuccessResult(); + NS_ENSURE_SUCCESS(rv, rv); + + Sequence storesToOpen; + nsRefPtr transaction = + IDBTransaction::Create(mDatabase, storesToOpen, + IDBTransaction::VERSION_CHANGE, true); + IDB_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsRefPtr helper = + new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion, + mCurrentVersion); + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + rv = quotaManager->AcquireExclusiveAccess( + mDatabase, mDatabase->Origin(), + Nullable(mDatabase->Type()), helper, + &VersionChangeEventsRunnable::QueueVersionChange, + helper); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + // The SetVersionHelper is responsible for dispatching us back to the + // main thread again and changing the state to eSetVersionCompleted. + mState = eSetVersionPending; + return NS_OK; +} + +nsresult +OpenDatabaseHelper::StartDelete() +{ + NS_ASSERTION(mState == eDeletePending, "Why are we here?"); + + // In case we fail, fire error events + mState = eFiringEvents; + + nsresult rv = EnsureSuccessResult(); + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr helper = + new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName, + mGroup, mASCIIOrigin, mPersistenceType); + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + rv = quotaManager->AcquireExclusiveAccess( + mDatabase, mDatabase->Origin(), + Nullable(mDatabase->Type()), helper, + &VersionChangeEventsRunnable::QueueVersionChange, + helper); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + // The DeleteDatabaseHelper is responsible for dispatching us back to the + // main thread again and changing the state to eDeleteCompleted. + mState = eDeletePending; + return NS_OK; +} + +NS_IMETHODIMP +OpenDatabaseHelper::Run() +{ + NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?"); + + if (NS_IsMainThread()) { + PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "Run", + js::ProfileEntry::Category::STORAGE); + + if (mState == eOpenPending) { + if (NS_FAILED(mResultCode)) { + return RunImmediately(); + } + + return DispatchToIOThread(); + } + + // If we need to queue up a SetVersionHelper, do that here. + if (mState == eSetVersionPending) { + nsresult rv = StartSetVersion(); + + if (NS_SUCCEEDED(rv)) { + return rv; + } + + SetError(rv); + // fall through and run the default error processing + } + else if (mState == eDeletePending) { + nsresult rv = StartDelete(); + + if (NS_SUCCEEDED(rv)) { + return rv; + } + + SetError(rv); + // fall through and run the default error processing + } + + // We've done whatever work we need to do on the DB thread, and any + // SetVersion/DeleteDatabase stuff is done by now. + NS_ASSERTION(mState == eFiringEvents || + mState == eSetVersionCompleted || + mState == eDeleteCompleted, "Why are we here?"); + + switch (mState) { + case eSetVersionCompleted: { + mState = eFiringEvents; + break; + } + + case eDeleteCompleted: { + // Destroy the database now (we should have the only ref). + mDatabase = nullptr; + + DatabaseInfo::Remove(mDatabaseId); + + mState = eFiringEvents; + break; + } + + case eFiringEvents: { + // Notify the request that we're done, but only if we didn't just + // finish a [SetVersion/DeleteDatabase]Helper. In that case, the + // helper tells the request that it is done, and we avoid calling + // NotifyHelperCompleted twice. + + nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this); + if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { + mResultCode = rv; + } + break; + } + + default: + NS_NOTREACHED("Shouldn't get here!"); + } + + NS_ASSERTION(mState == eFiringEvents, "Why are we here?"); + + IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread " + "response (rv = %lu)", + "IDBRequest[%llu] MT Done", + mRequest->GetSerialNumber(), mResultCode); + + if (NS_FAILED(mResultCode)) { + DispatchErrorEvent(); + } else { + DispatchSuccessEvent(); + } + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never be null!"); + + quotaManager-> + AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mASCIIOrigin), + Nullable(mPersistenceType), + mDatabaseId); + + ReleaseMainThreadObjects(); + + return NS_OK; + } + + PROFILER_LABEL("OpenDatabaseHelper", "Run", + js::ProfileEntry::Category::STORAGE); + + // We're on the DB thread. + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + IDB_PROFILER_MARK("IndexedDB Request %llu: Beginning database work", + "IDBRequest[%llu] DT Start", mRequest->GetSerialNumber()); + + NS_ASSERTION(mState == eDBWork, "Why are we here?"); + mResultCode = DoDatabaseWork(); + NS_ASSERTION(mState != eDBWork, "We should be doing something else now."); + + IDB_PROFILER_MARK("IndexedDB Request %llu: Finished database work (rv = %lu)", + "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(), + mResultCode); + + return NS_DispatchToMainThread(this); +} + +nsresult +OpenDatabaseHelper::EnsureSuccessResult() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "EnsureSuccessResult", + js::ProfileEntry::Category::STORAGE); + + nsRefPtr dbInfo; + if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) { + +#ifdef DEBUG + { + NS_ASSERTION(dbInfo->name == mName && + dbInfo->version == mCurrentVersion && + dbInfo->persistenceType == mPersistenceType && + dbInfo->id == mDatabaseId && + dbInfo->filePath == mDatabaseFilePath, + "Metadata mismatch!"); + + uint32_t objectStoreCount = mObjectStores.Length(); + for (uint32_t index = 0; index < objectStoreCount; index++) { + nsRefPtr& info = mObjectStores[index]; + + ObjectStoreInfo* otherInfo = dbInfo->GetObjectStore(info->name); + NS_ASSERTION(otherInfo, "ObjectStore not known!"); + + NS_ASSERTION(info->name == otherInfo->name && + info->id == otherInfo->id && + info->keyPath == otherInfo->keyPath, + "Metadata mismatch!"); + NS_ASSERTION(dbInfo->ContainsStoreName(info->name), + "Object store names out of date!"); + NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(), + "Bad index length!"); + + uint32_t indexCount = info->indexes.Length(); + for (uint32_t indexIndex = 0; indexIndex < indexCount; indexIndex++) { + const IndexInfo& indexInfo = info->indexes[indexIndex]; + const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex]; + NS_ASSERTION(indexInfo.id == otherIndexInfo.id, + "Bad index id!"); + NS_ASSERTION(indexInfo.name == otherIndexInfo.name, + "Bad index name!"); + NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath, + "Bad index keyPath!"); + NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique, + "Bad index unique value!"); + } + } + } +#endif + + } + else { + nsRefPtr newInfo(new DatabaseInfo()); + + newInfo->name = mName; + newInfo->group = mGroup; + newInfo->origin = mASCIIOrigin; + newInfo->persistenceType = mPersistenceType; + newInfo->id = mDatabaseId; + newInfo->filePath = mDatabaseFilePath; + + if (!DatabaseInfo::Put(newInfo)) { + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + newInfo.swap(dbInfo); + + nsresult rv = IDBFactory::SetDatabaseMetadata(dbInfo, mCurrentVersion, + mObjectStores); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!"); + } + + dbInfo->nextObjectStoreId = mLastObjectStoreId + 1; + dbInfo->nextIndexId = mLastIndexId + 1; + + nsRefPtr database = + IDBDatabase::Create(mOpenDBRequest, mOpenDBRequest->Factory(), + dbInfo.forget(), mASCIIOrigin, mFileManager, + mContentParent); + if (!database) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!"); + mDatabase.swap(database); + + return NS_OK; +} + +nsresult +OpenDatabaseHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + // Be careful not to load the database twice. + if (!mDatabase) { + nsresult rv = EnsureSuccessResult(); + NS_ENSURE_SUCCESS(rv, rv); + } + + return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), + aVal); +} + +nsresult +OpenDatabaseHelper::NotifySetVersionFinished() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); + NS_ASSERTION(mState = eSetVersionPending, "How did we get here?"); + + // Allow transaction creation to proceed. + mDatabase->ExitSetVersionTransaction(); + + mState = eSetVersionCompleted; + + // Dispatch ourself back to the main thread + return NS_DispatchToCurrentThread(this); +} + +nsresult +OpenDatabaseHelper::NotifyDeleteFinished() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); + NS_ASSERTION(mState == eDeletePending, "How did we get here?"); + + mState = eDeleteCompleted; + + // Dispatch ourself back to the main thread + return NS_DispatchToCurrentThread(this); +} + +void +OpenDatabaseHelper::BlockDatabase() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mDatabase, "This is going bad fast."); + + mDatabase->EnterSetVersionTransaction(); +} + +void +OpenDatabaseHelper::DispatchSuccessEvent() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "DispatchSuccessEvent", + js::ProfileEntry::Category::STORAGE); + + nsRefPtr event = + CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR), + eDoesNotBubble, eNotCancelable); + if (!event) { + NS_ERROR("Failed to create event!"); + return; + } + + bool dummy; + mOpenDBRequest->DispatchEvent(event, &dummy); +} + +void +OpenDatabaseHelper::DispatchErrorEvent() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "DispatchErrorEvent", + js::ProfileEntry::Category::STORAGE); + + nsRefPtr event = + CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(ERROR_EVT_STR), + eDoesBubble, eCancelable); + if (!event) { + NS_ERROR("Failed to create event!"); + return; + } + + ErrorResult rv; + nsRefPtr error = mOpenDBRequest->GetError(rv); + + NS_ASSERTION(!rv.Failed(), "This shouldn't be failing at this point!"); + if (!error) { + mOpenDBRequest->SetError(mResultCode); + } + + bool dummy; + mOpenDBRequest->DispatchEvent(event, &dummy); +} + +void +OpenDatabaseHelper::ReleaseMainThreadObjects() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mOpenDBRequest = nullptr; + mDatabase = nullptr; + + HelperBase::ReleaseMainThreadObjects(); +} + +NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper) + +nsresult +SetVersionHelper::Init() +{ + // Block transaction creation until we are done. + mOpenHelper->BlockDatabase(); + + return NS_OK; +} + +nsresult +SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(aConnection, "Passing a null connection!"); + + PROFILER_LABEL("SetVersionHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE database " + "SET version = :version" + ), getter_AddRefs(stmt)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"), + mRequestedVersion); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (NS_FAILED(stmt->Execute())) { + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + } + + return NS_OK; +} + +nsresult +SetVersionHelper::GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) +{ + DatabaseInfo* info = mDatabase->Info(); + info->version = mRequestedVersion; + + NS_ASSERTION(mTransaction, "Better have a transaction!"); + + mOpenRequest->SetTransaction(mTransaction); + + return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), + aVal); +} + +nsresult +SetVersionHelper::OnExclusiveAccessAcquired() +{ + nsresult rv = DispatchToTransactionPool(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +// static +template +void +VersionChangeEventsRunnable::QueueVersionChange( + nsTArray >& aDatabases, + void* aClosure) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?"); + + T* closure = static_cast(aClosure); + + nsRefPtr eventsRunnable = + new VersionChangeEventsRunnable(closure->mOpenHelper->Database(), + closure->mOpenRequest, + aDatabases, + closure->mCurrentVersion, + closure->RequestedVersion()); + + NS_DispatchToCurrentThread(eventsRunnable); +} + +already_AddRefed +SetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) +{ + NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?"); + + return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner, + mCurrentVersion, + mRequestedVersion); +} + +nsresult +SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "This is unexpected."); + NS_ASSERTION(mOpenRequest, "Why don't we have a request?"); + + return mOpenHelper->NotifySetVersionFinished(); +} + +nsresult +SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "This is unexpected."); + NS_ASSERTION(mOpenRequest, "Why don't we have a request?"); + + // If we hit an error, the OpenDatabaseHelper needs to get that error too. + nsresult rv = GetResultCode(); + if (NS_FAILED(rv)) { + mOpenHelper->SetError(rv); + } + + // If the transaction was aborted, we should throw an error message. + if (aTransaction->IsAborted()) { + mOpenHelper->SetError(aTransaction->GetAbortCode()); + } + + mOpenRequest->SetTransaction(nullptr); + mOpenRequest = nullptr; + + mOpenHelper = nullptr; + + return rv; +} + +NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper); + +nsresult +DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + NS_ASSERTION(!aConnection, "How did we get a connection here?"); + + PROFILER_LABEL("DeleteDatabaseHelper", "DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const StoragePrivilege& privilege = mOpenHelper->Privilege(); + + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never fail!"); + + nsCOMPtr directory; + nsresult rv = quotaManager->GetDirectoryForOrigin(mPersistenceType, + mASCIIOrigin, + getter_AddRefs(directory)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + NS_ASSERTION(directory, "What?"); + + rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsAutoString filename; + rv = GetDatabaseFilename(mName, filename); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + nsCOMPtr dbFile; + rv = directory->Clone(getter_AddRefs(dbFile)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + bool exists = false; + rv = dbFile->Exists(&exists); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (exists) { + int64_t fileSize; + + if (privilege != Chrome) { + rv = dbFile->GetFileSize(&fileSize); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + rv = dbFile->Remove(false); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (privilege != Chrome) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup, + mASCIIOrigin, fileSize); + } + } + + nsCOMPtr dbJournalFile; + rv = directory->Clone(getter_AddRefs(dbJournalFile)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbJournalFile->Append(filename + NS_LITERAL_STRING(".sqlite-journal")); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = dbJournalFile->Exists(&exists); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (exists) { + rv = dbJournalFile->Remove(false); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + nsCOMPtr fmDirectory; + rv = directory->Clone(getter_AddRefs(fmDirectory)); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = fmDirectory->Append(filename); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + rv = fmDirectory->Exists(&exists); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (exists) { + bool isDirectory; + rv = fmDirectory->IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + uint64_t usage = 0; + + if (privilege != Chrome) { + rv = FileManager::GetUsage(fmDirectory, &usage); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + rv = fmDirectory->Remove(true); + IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + if (privilege != Chrome) { + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "Shouldn't be null!"); + + quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup, + mASCIIOrigin, usage); + } + } + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + NS_ASSERTION(mgr, "This should never fail!"); + + mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName); + + return NS_OK; +} + +nsresult +DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) +{ + return NS_OK; +} + +nsresult +DeleteDatabaseHelper::OnExclusiveAccessAcquired() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "We should definitely have a manager here"); + + nsresult rv = Dispatch(quotaManager->IOThread()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +DeleteDatabaseHelper::Init() +{ + // Note that there's no need to block the database here, since the page + // never gets to touch it, and all other databases must be closed. + + return NS_OK; +} diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h new file mode 100644 index 000000000000..00a5a0fdd66d --- /dev/null +++ b/dom/indexedDB/OpenDatabaseHelper.h @@ -0,0 +1,175 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_opendatabasehelper_h__ +#define mozilla_dom_indexeddb_opendatabasehelper_h__ + +#include "AsyncConnectionHelper.h" + +#include "nsIRunnable.h" + +#include "mozilla/dom/quota/StoragePrivilege.h" + +#include "DatabaseInfo.h" +#include "IDBDatabase.h" +#include "IDBRequest.h" + +class mozIStorageConnection; + +namespace mozilla { +namespace dom { +class nsIContentParent; +} +} + +BEGIN_INDEXEDDB_NAMESPACE + +class CheckPermissionsHelper; + +class OpenDatabaseHelper : public HelperBase +{ + friend class CheckPermissionsHelper; + + typedef mozilla::dom::quota::PersistenceType PersistenceType; + typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege; + + ~OpenDatabaseHelper() {} + +public: + OpenDatabaseHelper(IDBOpenDBRequest* aRequest, + const nsAString& aName, + const nsACString& aGroup, + const nsACString& aASCIIOrigin, + uint64_t aRequestedVersion, + PersistenceType aPersistenceType, + bool aForDeletion, + mozilla::dom::nsIContentParent* aContentParent, + StoragePrivilege aPrivilege) + : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName), + mGroup(aGroup), mASCIIOrigin(aASCIIOrigin), + mRequestedVersion(aRequestedVersion), mPersistenceType(aPersistenceType), + mForDeletion(aForDeletion), mPrivilege(aPrivilege), + mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0), + mLastIndexId(0), mState(eCreated), mResultCode(NS_OK), + mLoadDBMetadata(false), + mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome) + { + NS_ASSERTION(!aForDeletion || !aRequestedVersion, + "Can't be for deletion and request a version!"); + } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE + + nsresult Init(); + + nsresult WaitForOpenAllowed(); + nsresult Dispatch(nsIEventTarget* aDatabaseThread); + nsresult DispatchToIOThread(); + nsresult RunImmediately(); + + void SetError(nsresult rv) + { + NS_ASSERTION(NS_FAILED(rv), "Why are you telling me?"); + mResultCode = rv; + } + + virtual nsresult GetResultCode() MOZ_OVERRIDE + { + return mResultCode; + } + + nsresult NotifySetVersionFinished(); + nsresult NotifyDeleteFinished(); + void BlockDatabase(); + + const nsACString& Id() const + { + return mDatabaseId; + } + + IDBDatabase* Database() const + { + NS_ASSERTION(mDatabase, "Calling at the wrong time!"); + return mDatabase; + } + + const StoragePrivilege& Privilege() const + { + return mPrivilege; + } + + static + nsresult CreateDatabaseConnection(nsIFile* aDBFile, + nsIFile* aFMDirectory, + const nsAString& aName, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + mozIStorageConnection** aConnection); + +protected: + // Methods only called on the main thread + nsresult EnsureSuccessResult(); + nsresult StartSetVersion(); + nsresult StartDelete(); + virtual nsresult GetSuccessResult(JSContext* aCx, + JS::MutableHandle aVal) MOZ_OVERRIDE; + void DispatchSuccessEvent(); + void DispatchErrorEvent(); + virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + + // Called by CheckPermissionsHelper on the main thread before dispatch. + void SetUnlimitedQuotaAllowed() + { + mTrackingQuota = false; + } + + // Methods only called on the DB thread + nsresult DoDatabaseWork(); + + // In-params. + nsRefPtr mOpenDBRequest; + nsString mName; + nsCString mGroup; + nsCString mASCIIOrigin; + uint64_t mRequestedVersion; + PersistenceType mPersistenceType; + bool mForDeletion; + StoragePrivilege mPrivilege; + nsCString mDatabaseId; + mozilla::dom::nsIContentParent* mContentParent; + + // Out-params. + nsTArray > mObjectStores; + uint64_t mCurrentVersion; + nsString mDatabaseFilePath; + int64_t mLastObjectStoreId; + int64_t mLastIndexId; + nsRefPtr mDatabase; + + // State variables + enum OpenDatabaseState { + eCreated = 0, // Not yet dispatched to the DB thread + eOpenPending, // Waiting for open allowed/open allowed + eDBWork, // Waiting to do/doing work on the DB thread + eFiringEvents, // Waiting to fire/firing events on the main thread + eSetVersionPending, // Waiting on a SetVersionHelper + eSetVersionCompleted, // SetVersionHelper is done + eDeletePending, // Waiting on a DeleteDatabaseHelper + eDeleteCompleted, // DeleteDatabaseHelper is done + }; + OpenDatabaseState mState; + nsresult mResultCode; + + nsRefPtr mFileManager; + + nsRefPtr mDBInfo; + bool mLoadDBMetadata; + bool mTrackingQuota; +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_opendatabasehelper_h__ diff --git a/dom/indexedDB/PBackgroundIDBCursor.ipdl b/dom/indexedDB/PBackgroundIDBCursor.ipdl deleted file mode 100644 index 3c755e903f26..000000000000 --- a/dom/indexedDB/PBackgroundIDBCursor.ipdl +++ /dev/null @@ -1,90 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackgroundIDBTransaction; -include protocol PBackgroundIDBVersionChangeTransaction; -include protocol PBlob; - -include PBackgroundIDBSharedTypes; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using struct mozilla::void_t - from "ipc/IPCMessageUtils.h"; - -using class mozilla::dom::indexedDB::Key - from "mozilla/dom/indexedDB/Key.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -struct ContinueParams -{ - Key key; -}; - -struct AdvanceParams -{ - uint32_t count; -}; - -union CursorRequestParams -{ - ContinueParams; - AdvanceParams; -}; - -struct ObjectStoreCursorResponse -{ - Key key; - SerializedStructuredCloneReadInfo cloneInfo; -}; - -struct ObjectStoreKeyCursorResponse -{ - Key key; -}; - -struct IndexCursorResponse -{ - Key key; - Key objectKey; - SerializedStructuredCloneReadInfo cloneInfo; -}; - -struct IndexKeyCursorResponse -{ - Key key; - Key objectKey; -}; - -union CursorResponse -{ - void_t; - nsresult; - ObjectStoreCursorResponse; - ObjectStoreKeyCursorResponse; - IndexCursorResponse; - IndexKeyCursorResponse; -}; - -protocol PBackgroundIDBCursor -{ - manager PBackgroundIDBTransaction or PBackgroundIDBVersionChangeTransaction; - -parent: - DeleteMe(); - - Continue(CursorRequestParams params); - -child: - __delete__(); - - Response(CursorResponse response); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBDatabase.ipdl b/dom/indexedDB/PBackgroundIDBDatabase.ipdl deleted file mode 100644 index 40cecd959c77..000000000000 --- a/dom/indexedDB/PBackgroundIDBDatabase.ipdl +++ /dev/null @@ -1,72 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackgroundIDBDatabaseFile; -include protocol PBackgroundIDBFactory; -include protocol PBackgroundIDBTransaction; -include protocol PBackgroundIDBVersionChangeTransaction; -include protocol PBlob; - -include InputStreamParams; -include PBackgroundIDBSharedTypes; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using struct mozilla::null_t - from "ipc/IPCMessageUtils.h"; - -using mozilla::dom::indexedDB::IDBTransaction::Mode - from "mozilla/dom/indexedDB/IDBTransaction.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -union NullableVersion -{ - null_t; - uint64_t; -}; - -union BlobOrInputStream -{ - PBlob; - InputStreamParams; -}; - -protocol PBackgroundIDBDatabase -{ - manager PBackgroundIDBFactory; - - manages PBackgroundIDBDatabaseFile; - manages PBackgroundIDBTransaction; - manages PBackgroundIDBVersionChangeTransaction; - -parent: - DeleteMe(); - - Blocked(); - - Close(); - - PBackgroundIDBDatabaseFile(BlobOrInputStream blobOrInputStream); - - PBackgroundIDBTransaction(nsString[] objectStoreNames, Mode mode); - -child: - __delete__(); - - VersionChange(uint64_t oldVersion, NullableVersion newVersion); - - Invalidate(); - - PBackgroundIDBVersionChangeTransaction(uint64_t currentVersion, - uint64_t requestedVersion, - int64_t nextObjectStoreId, - int64_t nextIndexId); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBFactory.ipdl b/dom/indexedDB/PBackgroundIDBFactory.ipdl deleted file mode 100644 index 1fac81f93b16..000000000000 --- a/dom/indexedDB/PBackgroundIDBFactory.ipdl +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackground; -include protocol PBackgroundIDBDatabase; -include protocol PBackgroundIDBFactoryRequest; - -include PBackgroundIDBSharedTypes; -include PBackgroundSharedTypes; - -using struct mozilla::void_t - from "ipc/IPCMessageUtils.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -struct CommonFactoryRequestParams -{ - DatabaseMetadata metadata; - PrincipalInfo principalInfo; - bool privateBrowsingMode; -}; - -struct OpenDatabaseRequestParams -{ - CommonFactoryRequestParams commonParams; -}; - -struct DeleteDatabaseRequestParams -{ - CommonFactoryRequestParams commonParams; -}; - -union FactoryRequestParams -{ - OpenDatabaseRequestParams; - DeleteDatabaseRequestParams; -}; - -protocol PBackgroundIDBFactory -{ - manager PBackground; - - manages PBackgroundIDBDatabase; - manages PBackgroundIDBFactoryRequest; - -parent: - DeleteMe(); - - PBackgroundIDBFactoryRequest(FactoryRequestParams params); - -child: - __delete__(); - - PBackgroundIDBDatabase(DatabaseSpec spec, - PBackgroundIDBFactoryRequest request); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl b/dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl deleted file mode 100644 index 1e198fc96bb3..000000000000 --- a/dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl +++ /dev/null @@ -1,48 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackgroundIDBFactory; -include protocol PBackgroundIDBDatabase; - -include PBackgroundSharedTypes; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -struct OpenDatabaseRequestResponse -{ - PBackgroundIDBDatabase database; -}; - -struct DeleteDatabaseRequestResponse -{ - uint64_t previousVersion; -}; - -union FactoryRequestResponse -{ - nsresult; - OpenDatabaseRequestResponse; - DeleteDatabaseRequestResponse; -}; - -protocol PBackgroundIDBFactoryRequest -{ - manager PBackgroundIDBFactory; - -child: - __delete__(FactoryRequestResponse response); - - PermissionChallenge(PrincipalInfo principalInfo); - - Blocked(uint64_t currentVersion); - -parent: - PermissionRetry(); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBRequest.ipdl b/dom/indexedDB/PBackgroundIDBRequest.ipdl deleted file mode 100644 index e63cfbd16563..000000000000 --- a/dom/indexedDB/PBackgroundIDBRequest.ipdl +++ /dev/null @@ -1,112 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackgroundIDBTransaction; -include protocol PBackgroundIDBVersionChangeTransaction; -include protocol PBlob; - -include PBackgroundIDBSharedTypes; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using struct mozilla::void_t - from "ipc/IPCMessageUtils.h"; - -using class mozilla::dom::indexedDB::Key - from "mozilla/dom/indexedDB/Key.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -struct ObjectStoreAddResponse -{ - Key key; -}; - -struct ObjectStorePutResponse -{ - Key key; -}; - -struct ObjectStoreGetResponse -{ - SerializedStructuredCloneReadInfo cloneInfo; -}; - -struct ObjectStoreGetAllResponse -{ - SerializedStructuredCloneReadInfo[] cloneInfos; -}; - -struct ObjectStoreGetAllKeysResponse -{ - Key[] keys; -}; - -struct ObjectStoreDeleteResponse -{ }; - -struct ObjectStoreClearResponse -{ }; - -struct ObjectStoreCountResponse -{ - uint64_t count; -}; - -struct IndexGetResponse -{ - SerializedStructuredCloneReadInfo cloneInfo; -}; - -struct IndexGetKeyResponse -{ - Key key; -}; - -struct IndexGetAllResponse -{ - SerializedStructuredCloneReadInfo[] cloneInfos; -}; - -struct IndexGetAllKeysResponse -{ - Key[] keys; -}; - -struct IndexCountResponse -{ - uint64_t count; -}; - -union RequestResponse -{ - nsresult; - ObjectStoreGetResponse; - ObjectStoreAddResponse; - ObjectStorePutResponse; - ObjectStoreDeleteResponse; - ObjectStoreClearResponse; - ObjectStoreCountResponse; - ObjectStoreGetAllResponse; - ObjectStoreGetAllKeysResponse; - IndexGetResponse; - IndexGetKeyResponse; - IndexGetAllResponse; - IndexGetAllKeysResponse; - IndexCountResponse; -}; - -protocol PBackgroundIDBRequest -{ - manager PBackgroundIDBTransaction or PBackgroundIDBVersionChangeTransaction; - -child: - __delete__(RequestResponse response); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh deleted file mode 100644 index d1e176906367..000000000000 --- a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh +++ /dev/null @@ -1,259 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBlob; -include protocol PBackgroundIDBDatabaseFile; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using struct mozilla::void_t - from "ipc/IPCMessageUtils.h"; - -using mozilla::dom::indexedDB::IDBCursor::Direction - from "mozilla/dom/indexedDB/IDBCursor.h"; - -using class mozilla::dom::indexedDB::Key - from "mozilla/dom/indexedDB/Key.h"; - -using class mozilla::dom::indexedDB::KeyPath - from "mozilla/dom/indexedDB/KeyPath.h"; - -using mozilla::dom::quota::PersistenceType - from "mozilla/dom/quota/PersistenceType.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -struct SerializedKeyRange -{ - Key lower; - Key upper; - bool lowerOpen; - bool upperOpen; - bool isOnly; -}; - -struct SerializedStructuredCloneReadInfo -{ - uint8_t[] data; - PBlob[] blobs; - - // This will only be valid for parent process actors. - intptr_t[] fileInfos; -}; - -struct SerializedStructuredCloneWriteInfo -{ - uint8_t[] data; - uint64_t offsetToKeyProp; -}; - -struct IndexUpdateInfo -{ - int64_t indexId; - Key value; -}; - -union OptionalKeyRange -{ - SerializedKeyRange; - void_t; -}; - -struct DatabaseMetadata -{ - nsString name; - uint64_t version; - PersistenceType persistenceType; - bool persistenceTypeIsExplicit; -}; - -struct ObjectStoreMetadata -{ - int64_t id; - nsString name; - KeyPath keyPath; - bool autoIncrement; -}; - -struct IndexMetadata -{ - int64_t id; - nsString name; - KeyPath keyPath; - bool unique; - bool multiEntry; -}; - -struct DatabaseSpec -{ - DatabaseMetadata metadata; - ObjectStoreSpec[] objectStores; -}; - -struct ObjectStoreSpec -{ - ObjectStoreMetadata metadata; - IndexMetadata[] indexes; -}; - -struct ObjectStoreOpenCursorParams -{ - int64_t objectStoreId; - OptionalKeyRange optionalKeyRange; - Direction direction; -}; - -struct ObjectStoreOpenKeyCursorParams -{ - int64_t objectStoreId; - OptionalKeyRange optionalKeyRange; - Direction direction; -}; - -struct IndexOpenCursorParams -{ - int64_t objectStoreId; - int64_t indexId; - OptionalKeyRange optionalKeyRange; - Direction direction; -}; - -struct IndexOpenKeyCursorParams -{ - int64_t objectStoreId; - int64_t indexId; - OptionalKeyRange optionalKeyRange; - Direction direction; -}; - -union OpenCursorParams -{ - ObjectStoreOpenCursorParams; - ObjectStoreOpenKeyCursorParams; - IndexOpenCursorParams; - IndexOpenKeyCursorParams; -}; - -// XXX Remove this once MutableFile has been ported to PBackground. -union DatabaseFileOrMutableFileId -{ - PBackgroundIDBDatabaseFile; - int64_t; -}; - -struct ObjectStoreAddPutParams -{ - int64_t objectStoreId; - SerializedStructuredCloneWriteInfo cloneInfo; - Key key; - IndexUpdateInfo[] indexUpdateInfos; - DatabaseFileOrMutableFileId[] files; -}; - -struct ObjectStoreAddParams -{ - ObjectStoreAddPutParams commonParams; -}; - -struct ObjectStorePutParams -{ - ObjectStoreAddPutParams commonParams; -}; - -struct ObjectStoreGetParams -{ - int64_t objectStoreId; - SerializedKeyRange keyRange; -}; - -struct ObjectStoreGetAllParams -{ - int64_t objectStoreId; - OptionalKeyRange optionalKeyRange; - uint32_t limit; -}; - -struct ObjectStoreGetAllKeysParams -{ - int64_t objectStoreId; - OptionalKeyRange optionalKeyRange; - uint32_t limit; -}; - -struct ObjectStoreDeleteParams -{ - int64_t objectStoreId; - SerializedKeyRange keyRange; -}; - -struct ObjectStoreClearParams -{ - int64_t objectStoreId; -}; - -struct ObjectStoreCountParams -{ - int64_t objectStoreId; - OptionalKeyRange optionalKeyRange; -}; - -struct IndexGetParams -{ - int64_t objectStoreId; - int64_t indexId; - SerializedKeyRange keyRange; -}; - -struct IndexGetKeyParams -{ - int64_t objectStoreId; - int64_t indexId; - SerializedKeyRange keyRange; -}; - -struct IndexGetAllParams -{ - int64_t objectStoreId; - int64_t indexId; - OptionalKeyRange optionalKeyRange; - uint32_t limit; -}; - -struct IndexGetAllKeysParams -{ - int64_t objectStoreId; - int64_t indexId; - OptionalKeyRange optionalKeyRange; - uint32_t limit; -}; - -struct IndexCountParams -{ - int64_t objectStoreId; - int64_t indexId; - OptionalKeyRange optionalKeyRange; -}; - -union RequestParams -{ - ObjectStoreAddParams; - ObjectStorePutParams; - ObjectStoreGetParams; - ObjectStoreGetAllParams; - ObjectStoreGetAllKeysParams; - ObjectStoreDeleteParams; - ObjectStoreClearParams; - ObjectStoreCountParams; - IndexGetParams; - IndexGetKeyParams; - IndexGetAllParams; - IndexGetAllKeysParams; - IndexCountParams; -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBTransaction.ipdl b/dom/indexedDB/PBackgroundIDBTransaction.ipdl deleted file mode 100644 index 0db17629207c..000000000000 --- a/dom/indexedDB/PBackgroundIDBTransaction.ipdl +++ /dev/null @@ -1,41 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackgroundIDBCursor; -include protocol PBackgroundIDBDatabase; -include protocol PBackgroundIDBDatabaseFile; -include protocol PBackgroundIDBRequest; - -include PBackgroundIDBSharedTypes; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -protocol PBackgroundIDBTransaction -{ - manager PBackgroundIDBDatabase; - - manages PBackgroundIDBCursor; - manages PBackgroundIDBRequest; - -parent: - DeleteMe(); - - Commit(); - Abort(nsresult resultCode); - - PBackgroundIDBCursor(OpenCursorParams params); - - PBackgroundIDBRequest(RequestParams params); - -child: - __delete__(); - - Complete(nsresult result); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl b/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl deleted file mode 100644 index f0b475f4a4a7..000000000000 --- a/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl +++ /dev/null @@ -1,49 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackgroundIDBCursor; -include protocol PBackgroundIDBDatabase; -include protocol PBackgroundIDBRequest; -include protocol PBlob; - -include PBackgroundIDBSharedTypes; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -protocol PBackgroundIDBVersionChangeTransaction -{ - manager PBackgroundIDBDatabase; - - manages PBackgroundIDBCursor; - manages PBackgroundIDBRequest; - -parent: - DeleteMe(); - - Commit(); - Abort(nsresult resultCode); - - CreateObjectStore(ObjectStoreMetadata metadata); - DeleteObjectStore(int64_t objectStoreId); - - CreateIndex(int64_t objectStoreId, - IndexMetadata metadata); - DeleteIndex(int64_t objectStoreId, - int64_t indexId); - - PBackgroundIDBCursor(OpenCursorParams params); - - PBackgroundIDBRequest(RequestParams params); - -child: - __delete__(); - - Complete(nsresult result); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PIndexedDBPermissionRequest.ipdl b/dom/indexedDB/PIndexedDBPermissionRequest.ipdl deleted file mode 100644 index 4eecb2b1c0ad..000000000000 --- a/dom/indexedDB/PIndexedDBPermissionRequest.ipdl +++ /dev/null @@ -1,27 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBrowser; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -protocol PIndexedDBPermissionRequest -{ - manager PBrowser; - -child: - /** - * Called when the user makes a choice or the permission request times out. - * - * @param permission - * The permission result (see nsIPermissionManager.idl for valid values). - */ - __delete__(uint32_t permission); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PermissionRequestBase.cpp b/dom/indexedDB/PermissionRequestBase.cpp deleted file mode 100644 index 9f7f38e8ba2b..000000000000 --- a/dom/indexedDB/PermissionRequestBase.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "PermissionRequestBase.h" - -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" -#include "mozilla/Services.h" -#include "nsIDOMWindow.h" -#include "nsIObserverService.h" -#include "nsIPrincipal.h" -#include "nsPIDOMWindow.h" -#include "nsXULAppAPI.h" - -namespace mozilla { -namespace dom { -namespace indexedDB { - -using namespace mozilla::services; - -namespace { - -#define IDB_PREFIX "indexedDB" -#define TOPIC_PREFIX IDB_PREFIX "-permissions-" - -const char kPermissionString[] = IDB_PREFIX; - -const char kPermissionPromptTopic[] = TOPIC_PREFIX "prompt"; -const char kPermissionResponseTopic[] = TOPIC_PREFIX "response"; - -#undef TOPIC_PREFIX -#undef IDB_PREFIX - -const uint32_t kPermissionDefault = nsIPermissionManager::UNKNOWN_ACTION; - -void -AssertSanity() -{ - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - MOZ_ASSERT(NS_IsMainThread()); -} - -} // anonymous namespace - -PermissionRequestBase::PermissionRequestBase(nsPIDOMWindow* aWindow, - nsIPrincipal* aPrincipal) - : mWindow(aWindow) - , mPrincipal(aPrincipal) -{ - AssertSanity(); - MOZ_ASSERT(aWindow); - MOZ_ASSERT(aPrincipal); -} - -PermissionRequestBase::~PermissionRequestBase() -{ - AssertSanity(); -} - -// static -nsresult -PermissionRequestBase::GetCurrentPermission(nsIPrincipal* aPrincipal, - PermissionValue* aCurrentValue) -{ - AssertSanity(); - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aCurrentValue); - - nsCOMPtr permMan = GetPermissionManager(); - if (NS_WARN_IF(!permMan)) { - return NS_ERROR_FAILURE; - } - - uint32_t intPermission; - nsresult rv = permMan->TestExactPermissionFromPrincipal( - aPrincipal, - kPermissionString, - &intPermission); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - PermissionValue permission = - PermissionValueForIntPermission(intPermission); - - MOZ_ASSERT(permission == kPermissionAllowed || - permission == kPermissionDenied || - permission == kPermissionPrompt); - - *aCurrentValue = permission; - return NS_OK; -} - -// static -auto -PermissionRequestBase::PermissionValueForIntPermission(uint32_t aIntPermission) - -> PermissionValue -{ - AssertSanity(); - - // The 'indexedDB' permission is unusual in that the default action is to - // allow access. Switch that here to make the logic clearer. - switch (aIntPermission) { - case kPermissionDefault: - return kPermissionAllowed; - case kPermissionAllowed: - return kPermissionPrompt; - case kPermissionDenied: - return kPermissionDenied; - default: - MOZ_CRASH("Bad permission!"); - } - - MOZ_CRASH("Should never get here!"); -} - -nsresult -PermissionRequestBase::PromptIfNeeded(PermissionValue* aCurrentValue) -{ - AssertSanity(); - MOZ_ASSERT(aCurrentValue); - MOZ_ASSERT(mPrincipal); - - // Tricky, we want to release the window and principal in all cases except - // when we successfully prompt. - nsCOMPtr window; - mWindow.swap(window); - - nsCOMPtr principal; - mPrincipal.swap(principal); - - PermissionValue currentValue; - nsresult rv = GetCurrentPermission(principal, ¤tValue); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(currentValue != kPermissionDefault); - - if (currentValue == kPermissionPrompt) { - nsCOMPtr obsSvc = GetObserverService(); - if (NS_WARN_IF(!obsSvc)) { - return NS_ERROR_FAILURE; - } - - // We're about to prompt so swap the members back. - window.swap(mWindow); - principal.swap(mPrincipal); - - rv = obsSvc->NotifyObservers(static_cast(this), - kPermissionPromptTopic, - nullptr); - if (NS_WARN_IF(NS_FAILED(rv))) { - // Finally release if we failed the prompt. - mWindow = nullptr; - mPrincipal = nullptr; - return rv; - } - } - - *aCurrentValue = currentValue; - return NS_OK; -} - -void -PermissionRequestBase::SetExplicitPermission(nsIPrincipal* aPrincipal, - uint32_t aIntPermission) -{ - AssertSanity(); - MOZ_ASSERT(aPrincipal); - MOZ_ASSERT(aIntPermission == kPermissionAllowed || - aIntPermission == kPermissionDenied); - - nsCOMPtr permMan = GetPermissionManager(); - if (NS_WARN_IF(!permMan)) { - return; - } - - nsresult rv = aIntPermission == kPermissionAllowed ? - permMan->RemoveFromPrincipal(aPrincipal, kPermissionString) : - permMan->AddFromPrincipal(aPrincipal, - kPermissionString, - aIntPermission, - nsIPermissionManager::EXPIRE_NEVER, - /* aExpireTime */ 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } -} - -NS_IMPL_ISUPPORTS(PermissionRequestBase, nsIObserver, nsIInterfaceRequestor) - -NS_IMETHODIMP -PermissionRequestBase::GetInterface(const nsIID& aIID, - void** aResult) -{ - AssertSanity(); - - if (aIID.Equals(NS_GET_IID(nsIObserver))) { - return QueryInterface(aIID, aResult); - } - - if (aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { - return mWindow->QueryInterface(aIID, aResult); - } - - *aResult = nullptr; - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -PermissionRequestBase::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - AssertSanity(); - MOZ_ASSERT(!strcmp(aTopic, kPermissionResponseTopic)); - MOZ_ASSERT(mWindow); - MOZ_ASSERT(mPrincipal); - - nsCOMPtr window; - mWindow.swap(window); - - nsCOMPtr principal; - mPrincipal.swap(principal); - - nsresult rv; - uint32_t promptResult = nsDependentString(aData).ToInteger(&rv); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); - - // The UI prompt code will only return one of these three values. We have to - // transform it to our values. - MOZ_ASSERT(promptResult == kPermissionDefault || - promptResult == kPermissionAllowed || - promptResult == kPermissionDenied); - - if (promptResult != kPermissionDefault) { - // Save explicitly allowed or denied permissions now. - SetExplicitPermission(principal, promptResult); - } - - PermissionValue permission; - switch (promptResult) { - case kPermissionDefault: - permission = kPermissionPrompt; - break; - - case kPermissionAllowed: - permission = kPermissionAllowed; - break; - - case kPermissionDenied: - permission = kPermissionDenied; - break; - - default: - MOZ_CRASH("Bad prompt result!"); - } - - OnPromptComplete(permission); - return NS_OK; -} - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/PermissionRequestBase.h b/dom/indexedDB/PermissionRequestBase.h deleted file mode 100644 index 3ebf9b81f559..000000000000 --- a/dom/indexedDB/PermissionRequestBase.h +++ /dev/null @@ -1,77 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_permissionrequestbase_h__ -#define mozilla_dom_indexeddb_permissionrequestbase_h__ - -#include "mozilla/Attributes.h" -#include "nsCOMPtr.h" -#include "nsIInterfaceRequestor.h" -#include "nsIObserver.h" -#include "nsIPermissionManager.h" -#include "nsISupportsImpl.h" -#include "nsString.h" - -class nsIPrincipal; -class nsPIDOMWindow; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -class PermissionRequestBase - : public nsIObserver - , public nsIInterfaceRequestor -{ - nsCOMPtr mWindow; - nsCOMPtr mPrincipal; - -public: - enum PermissionValue { - kPermissionAllowed = nsIPermissionManager::ALLOW_ACTION, - kPermissionDenied = nsIPermissionManager::DENY_ACTION, - kPermissionPrompt = nsIPermissionManager::PROMPT_ACTION - }; - - NS_DECL_ISUPPORTS - - // This function will not actually prompt. It will never return - // kPermissionDefault but will instead translate the permission manager value - // into the correct value for the given type. - static nsresult - GetCurrentPermission(nsIPrincipal* aPrincipal, - PermissionValue* aCurrentValue); - - static PermissionValue - PermissionValueForIntPermission(uint32_t aIntPermission); - - // This function will prompt if needed. It may only be called once. - nsresult - PromptIfNeeded(PermissionValue* aCurrentValue); - -protected: - PermissionRequestBase(nsPIDOMWindow* aWindow, - nsIPrincipal* aPrincipal); - - // Reference counted. - virtual - ~PermissionRequestBase(); - - virtual void - OnPromptComplete(PermissionValue aPermissionValue) = 0; - -private: - void - SetExplicitPermission(nsIPrincipal* aPrincipal, - uint32_t aIntPermission); - - NS_DECL_NSIOBSERVER - NS_DECL_NSIINTERFACEREQUESTOR -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_indexeddb_permissionrequestbase_h__ diff --git a/dom/indexedDB/ProfilerHelpers.h b/dom/indexedDB/ProfilerHelpers.h index 4afab8469c65..46752955d4d9 100644 --- a/dom/indexedDB/ProfilerHelpers.h +++ b/dom/indexedDB/ProfilerHelpers.h @@ -36,9 +36,7 @@ #include "IDBTransaction.h" #include "Key.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE class ProfilerString : public nsAutoCString { @@ -161,9 +159,7 @@ public: } }; -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #define IDB_PROFILER_MARK(_detailedFmt, _conciseFmt, ...) \ do { \ diff --git a/dom/indexedDB/ReportInternalError.cpp b/dom/indexedDB/ReportInternalError.cpp index 1441099ec7bd..d02eedc30440 100644 --- a/dom/indexedDB/ReportInternalError.cpp +++ b/dom/indexedDB/ReportInternalError.cpp @@ -11,9 +11,7 @@ #include "nsContentUtils.h" #include "nsPrintfCString.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) @@ -31,6 +29,4 @@ ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) "indexedDB"); } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/ReportInternalError.h b/dom/indexedDB/ReportInternalError.h index aa014a091a8f..882c1469fd44 100644 --- a/dom/indexedDB/ReportInternalError.h +++ b/dom/indexedDB/ReportInternalError.h @@ -41,15 +41,11 @@ } while(0) -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr); -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_reportinternalerror_h__ diff --git a/dom/indexedDB/SerializationHelpers.h b/dom/indexedDB/SerializationHelpers.h deleted file mode 100644 index 45cb5b6fb78a..000000000000 --- a/dom/indexedDB/SerializationHelpers.h +++ /dev/null @@ -1,103 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_serializationhelpers_h__ -#define mozilla_dom_indexeddb_serializationhelpers_h__ - -#include "ipc/IPCMessageUtils.h" - -#include "mozilla/dom/indexedDB/Key.h" -#include "mozilla/dom/indexedDB/KeyPath.h" -#include "mozilla/dom/indexedDB/IDBCursor.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" -#include "mozilla/dom/quota/PersistenceType.h" -#include "mozilla/dom/quota/StoragePrivilege.h" - -namespace IPC { - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::quota::PersistenceType, - mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT, - mozilla::dom::quota::PERSISTENCE_TYPE_INVALID> -{ }; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer -{ }; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::Key paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mBuffer); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->mBuffer); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.mBuffer, aLog); - } -}; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer -{ }; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::KeyPath paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mType); - WriteParam(aMsg, aParam.mStrings); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->mType) && - ReadParam(aMsg, aIter, &aResult->mStrings); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.mStrings, aLog); - } -}; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::indexedDB::IDBCursor::Direction, - mozilla::dom::indexedDB::IDBCursor::NEXT, - mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID> -{ }; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::indexedDB::IDBTransaction::Mode, - mozilla::dom::indexedDB::IDBTransaction::READ_ONLY, - mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID> -{ }; - -} // namespace IPC - -#endif // mozilla_dom_indexeddb_serializationhelpers_h__ diff --git a/dom/indexedDB/TransactionThreadPool.cpp b/dom/indexedDB/TransactionThreadPool.cpp index a8732dddc959..6a59f5b3d997 100644 --- a/dom/indexedDB/TransactionThreadPool.cpp +++ b/dom/indexedDB/TransactionThreadPool.cpp @@ -6,25 +6,19 @@ #include "TransactionThreadPool.h" -#include "IDBTransaction.h" -#include "mozilla/Monitor.h" -#include "mozilla/Move.h" -#include "mozilla/ipc/BackgroundParent.h" -#include "nsComponentManagerUtils.h" -#include "nsIEventTarget.h" -#include "nsIRunnable.h" -#include "nsISupportsPriority.h" +#include "nsIObserverService.h" #include "nsIThreadPool.h" + +#include "nsComponentManagerUtils.h" #include "nsThreadUtils.h" #include "nsServiceManagerUtils.h" #include "nsXPCOMCIDInternal.h" + #include "ProfilerHelpers.h" -namespace mozilla { -namespace dom { -namespace indexedDB { +using mozilla::MonitorAutoLock; -using mozilla::ipc::AssertIsOnBackgroundThread; +USING_INDEXEDDB_NAMESPACE namespace { @@ -32,414 +26,168 @@ const uint32_t kThreadLimit = 20; const uint32_t kIdleThreadLimit = 5; const uint32_t kIdleThreadTimeoutMs = 30000; -#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS) -#define BUILD_THREADPOOL_LISTENER -#endif +TransactionThreadPool* gThreadPool = nullptr; +bool gShutdown = false; -#ifdef DEBUG +#ifdef MOZ_ENABLE_PROFILER_SPS -const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; -const uint32_t kDEBUGThreadSleepMS = 0; - -#endif // DEBUG - -#ifdef BUILD_THREADPOOL_LISTENER - -class TransactionThreadPoolListener MOZ_FINAL - : public nsIThreadPoolListener +class TransactionThreadPoolListener : public nsIThreadPoolListener { public: - TransactionThreadPoolListener() - { } - NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITHREADPOOLLISTENER private: virtual ~TransactionThreadPoolListener() { } - - NS_DECL_NSITHREADPOOLLISTENER }; -#endif // BUILD_THREADPOOL_LISTENER +#endif // MOZ_ENABLE_PROFILER_SPS } // anonymous namespace -class TransactionThreadPool::FinishTransactionRunnable MOZ_FINAL - : public nsRunnable +BEGIN_INDEXEDDB_NAMESPACE + +class FinishTransactionRunnable MOZ_FINAL : public nsIRunnable { - typedef TransactionThreadPool::FinishCallback FinishCallback; - - nsRefPtr mThreadPool; - nsRefPtr mFinishCallback; - uint64_t mTransactionId; - const nsCString mDatabaseId; - const nsTArray mObjectStoreNames; - uint16_t mMode; - public: - FinishTransactionRunnable(already_AddRefed aThreadPool, - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode, - already_AddRefed aFinishCallback); - - void - Dispatch() - { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - mThreadPool->mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL))); - } - - NS_DECL_ISUPPORTS_INHERITED - -private: - ~FinishTransactionRunnable() - { } - + NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE -}; -struct TransactionThreadPool::DatabaseTransactionInfo MOZ_FINAL -{ - typedef nsClassHashtable - TransactionHashtable; - TransactionHashtable transactions; - nsClassHashtable blockingTransactions; - - DatabaseTransactionInfo() - { - MOZ_COUNT_CTOR(DatabaseTransactionInfo); - } - - ~DatabaseTransactionInfo() - { - MOZ_COUNT_DTOR(DatabaseTransactionInfo); - } -}; - -struct TransactionThreadPool::DatabasesCompleteCallback MOZ_FINAL -{ - friend class nsAutoPtr; - - nsTArray mDatabaseIds; - nsCOMPtr mCallback; - - DatabasesCompleteCallback() - { - MOZ_COUNT_CTOR(DatabasesCompleteCallback); - } + inline FinishTransactionRunnable(IDBTransaction* aTransaction, + nsCOMPtr& aFinishRunnable); private: - ~DatabasesCompleteCallback() - { - MOZ_COUNT_DTOR(DatabasesCompleteCallback); - } + ~FinishTransactionRunnable() {} + + IDBTransaction* mTransaction; + nsCOMPtr mFinishRunnable; }; -class TransactionThreadPool::TransactionQueue MOZ_FINAL - : public nsRunnable -{ - Monitor mMonitor; - - nsRefPtr mOwningThreadPool; - uint64_t mTransactionId; - const nsCString mDatabaseId; - const nsTArray mObjectStoreNames; - uint16_t mMode; - - nsAutoTArray, 10> mQueue; - nsRefPtr mFinishCallback; - bool mShouldFinish; - -public: - TransactionQueue(TransactionThreadPool* aThreadPool, - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode); - - NS_DECL_ISUPPORTS_INHERITED - - void Unblock(); - - void Dispatch(nsIRunnable* aRunnable); - - void Finish(FinishCallback* aFinishCallback); - -private: - ~TransactionQueue() - { } - - NS_DECL_NSIRUNNABLE -}; - -struct TransactionThreadPool::TransactionInfo MOZ_FINAL -{ - uint64_t transactionId; - nsCString databaseId; - nsRefPtr queue; - nsTHashtable> blockedOn; - nsTHashtable> blocking; - - TransactionInfo(TransactionThreadPool* aThreadPool, - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode) - : transactionId(aTransactionId), databaseId(aDatabaseId) - { - MOZ_COUNT_CTOR(TransactionInfo); - - queue = new TransactionQueue(aThreadPool, aTransactionId, aDatabaseId, - aObjectStoreNames, aMode); - } - - ~TransactionInfo() - { - MOZ_COUNT_DTOR(TransactionInfo); - } -}; - -struct TransactionThreadPool::TransactionInfoPair MOZ_FINAL -{ - // Multiple reading transactions can block future writes. - nsTArray lastBlockingWrites; - // But only a single writing transaction can block future reads. - TransactionInfo* lastBlockingReads; - - TransactionInfoPair() - : lastBlockingReads(nullptr) - { - MOZ_COUNT_CTOR(TransactionInfoPair); - } - - ~TransactionInfoPair() - { - MOZ_COUNT_DTOR(TransactionInfoPair); - } -}; +END_INDEXEDDB_NAMESPACE TransactionThreadPool::TransactionThreadPool() - : mOwningThread(NS_GetCurrentThread()) - , mNextTransactionId(0) - , mShutdownRequested(false) - , mShutdownComplete(false) { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mOwningThread); - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!gThreadPool, "More than one instance!"); } TransactionThreadPool::~TransactionThreadPool() { - AssertIsOnOwningThread(); - MOZ_ASSERT(HasCompletedShutdown()); -} - -#ifdef DEBUG - -void -TransactionThreadPool::AssertIsOnOwningThread() const -{ - MOZ_ASSERT(mOwningThread); - - bool current; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); - MOZ_ASSERT(current); -} - -#endif // DEBUG - -// static -already_AddRefed -TransactionThreadPool::Create() -{ - AssertIsOnBackgroundThread(); - - nsRefPtr threadPool = new TransactionThreadPool(); - threadPool->AssertIsOnOwningThread(); - - if (NS_WARN_IF(NS_FAILED(threadPool->Init()))) { - threadPool->ShutdownAsync(); - return nullptr; - } - - return threadPool.forget(); -} - -void -TransactionThreadPool::ShutdownAndSpin() -{ - AssertIsOnOwningThread(); - - if (mShutdownComplete) { - return; - } - - ShutdownAsync(); - - nsIThread* currentThread = NS_GetCurrentThread(); - MOZ_ASSERT(currentThread); - - while (!mShutdownComplete) { - MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); - } -} - -void -TransactionThreadPool::ShutdownAsync() -{ - AssertIsOnOwningThread(); - - if (mShutdownRequested) { - return; - } - - mShutdownRequested = true; - - if (!mThreadPool) { - MOZ_ASSERT(!mTransactionsInProgress.Count()); - MOZ_ASSERT(mCompleteCallbacks.IsEmpty()); - - mShutdownComplete = true; - return; - } - - if (!mTransactionsInProgress.Count()) { - CleanupAsync(); - } -} - -bool -TransactionThreadPool::HasCompletedShutdown() const -{ - AssertIsOnOwningThread(); - MOZ_ASSERT_IF(mShutdownComplete, mShutdownRequested); - - return mShutdownComplete; + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(gThreadPool == this, "Different instances!"); + gThreadPool = nullptr; } // static -uint64_t -TransactionThreadPool::NextTransactionId() +TransactionThreadPool* +TransactionThreadPool::GetOrCreate() { - AssertIsOnOwningThread(); - MOZ_ASSERT(mNextTransactionId < UINT64_MAX); + if (!gThreadPool && !gShutdown) { + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + nsAutoPtr pool(new TransactionThreadPool()); - return ++mNextTransactionId; + nsresult rv = pool->Init(); + NS_ENSURE_SUCCESS(rv, nullptr); + + gThreadPool = pool.forget(); + } + return gThreadPool; +} + +// static +TransactionThreadPool* +TransactionThreadPool::Get() +{ + return gThreadPool; +} + +// static +void +TransactionThreadPool::Shutdown() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + gShutdown = true; + + if (gThreadPool) { + if (NS_FAILED(gThreadPool->Cleanup())) { + NS_WARNING("Failed to shutdown thread pool!"); + } + delete gThreadPool; + gThreadPool = nullptr; + } } nsresult TransactionThreadPool::Init() { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsresult rv; mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); rv = mThreadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Trans")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); rv = mThreadPool->SetThreadLimit(kThreadLimit); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + NS_ENSURE_SUCCESS(rv, rv); -#ifdef BUILD_THREADPOOL_LISTENER +#ifdef MOZ_ENABLE_PROFILER_SPS nsCOMPtr listener = new TransactionThreadPoolListener(); rv = mThreadPool->SetListener(listener); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } -#endif // BUILD_THREADPOOL_LISTENER - - NS_WARN_IF_FALSE(!kDEBUGThreadSleepMS, - "TransactionThreadPool thread debugging enabled, sleeping " - "after every event!"); + NS_ENSURE_SUCCESS(rv, rv); +#endif return NS_OK; } -void +nsresult TransactionThreadPool::Cleanup() { - AssertIsOnOwningThread(); - MOZ_ASSERT(mThreadPool); - MOZ_ASSERT(mShutdownRequested); - MOZ_ASSERT(!mShutdownComplete); - MOZ_ASSERT(!mTransactionsInProgress.Count()); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThreadPool->Shutdown())); + PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "Cleanup", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = mThreadPool->Shutdown(); + NS_ENSURE_SUCCESS(rv, rv); + + // Make sure the pool is still accessible while any callbacks generated from + // the other threads are processed. + rv = NS_ProcessPendingEvents(nullptr); + NS_ENSURE_SUCCESS(rv, rv); if (!mCompleteCallbacks.IsEmpty()) { // Run all callbacks manually now. - for (uint32_t count = mCompleteCallbacks.Length(), index = 0; - index < count; - index++) { - nsAutoPtr& completeCallback = - mCompleteCallbacks[index]; - MOZ_ASSERT(completeCallback); - MOZ_ASSERT(completeCallback->mCallback); - - completeCallback->mCallback->Run(); - - completeCallback = nullptr; + for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) { + mCompleteCallbacks[index].mCallback->Run(); } - mCompleteCallbacks.Clear(); // And make sure they get processed. - nsIThread* currentThread = NS_GetCurrentThread(); - MOZ_ASSERT(currentThread); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProcessPendingEvents(currentThread))); + rv = NS_ProcessPendingEvents(nullptr); + NS_ENSURE_SUCCESS(rv, rv); } - mShutdownComplete = true; -} - -void -TransactionThreadPool::CleanupAsync() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mThreadPool); - MOZ_ASSERT(mShutdownRequested); - MOZ_ASSERT(!mShutdownComplete); - MOZ_ASSERT(!mTransactionsInProgress.Count()); - - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &TransactionThreadPool::Cleanup); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable))); + return NS_OK; } // static PLDHashOperator -TransactionThreadPool::MaybeUnblockTransaction( - nsPtrHashKey* aKey, - void* aUserArg) +TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey* aKey, + void* aUserArg) { - AssertIsOnBackgroundThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); TransactionInfo* maybeUnblockedInfo = aKey->GetKey(); TransactionInfo* finishedInfo = static_cast(aUserArg); @@ -456,20 +204,21 @@ TransactionThreadPool::MaybeUnblockTransaction( } void -TransactionThreadPool::FinishTransaction( - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode) +TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) { - AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "Null pointer!"); - PROFILER_LABEL("IndexedDB", - "TransactionThreadPool::FinishTransaction", - js::ProfileEntry::Category::STORAGE); + PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "FinishTransaction", + js::ProfileEntry::Category::STORAGE); + + // AddRef here because removing from the hash will call Release. + nsRefPtr transaction(aTransaction); + + const nsACString& databaseId = aTransaction->mDatabase->Id(); DatabaseTransactionInfo* dbTransactionInfo; - if (!mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) { + if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { NS_ERROR("We don't know anyting about this database?!"); return; } @@ -480,7 +229,7 @@ TransactionThreadPool::FinishTransaction( uint32_t transactionCount = transactionsInProgress.Count(); #ifdef DEBUG - if (aMode == IDBTransaction::VERSION_CHANGE) { + if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { NS_ASSERTION(transactionCount == 1, "More transactions running than should be!"); } @@ -489,40 +238,36 @@ TransactionThreadPool::FinishTransaction( if (transactionCount == 1) { #ifdef DEBUG { - const TransactionInfo* info = transactionsInProgress.Get(aTransactionId); - NS_ASSERTION(info->transactionId == aTransactionId, "Transaction mismatch!"); + const TransactionInfo* info = transactionsInProgress.Get(aTransaction); + NS_ASSERTION(info->transaction == aTransaction, "Transaction mismatch!"); } #endif - mTransactionsInProgress.Remove(aDatabaseId); + mTransactionsInProgress.Remove(databaseId); // See if we need to fire any complete callbacks. uint32_t index = 0; while (index < mCompleteCallbacks.Length()) { if (MaybeFireCallback(mCompleteCallbacks[index])) { mCompleteCallbacks.RemoveElementAt(index); - } else { + } + else { index++; } } - if (mShutdownRequested) { - CleanupAsync(); - } - return; } - - TransactionInfo* info = transactionsInProgress.Get(aTransactionId); + TransactionInfo* info = transactionsInProgress.Get(aTransaction); NS_ASSERTION(info, "We've never heard of this transaction?!?"); - const nsTArray& objectStoreNames = aObjectStoreNames; + const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; for (size_t index = 0, count = objectStoreNames.Length(); index < count; index++) { TransactionInfoPair* blockInfo = dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); NS_ASSERTION(blockInfo, "Huh?"); - if (aMode == IDBTransaction::READ_WRITE && + if (aTransaction->mMode == IDBTransaction::READ_WRITE && blockInfo->lastBlockingReads == info) { blockInfo->lastBlockingReads = nullptr; } @@ -535,78 +280,48 @@ TransactionThreadPool::FinishTransaction( info->blocking.EnumerateEntries(MaybeUnblockTransaction, info); - transactionsInProgress.Remove(aTransactionId); -} - -TransactionThreadPool::TransactionQueue* -TransactionThreadPool::GetQueueForTransaction(uint64_t aTransactionId, - const nsACString& aDatabaseId) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aTransactionId <= mNextTransactionId); - - DatabaseTransactionInfo* dbTransactionInfo; - if (mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) { - DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = - dbTransactionInfo->transactions; - TransactionInfo* info = transactionsInProgress.Get(aTransactionId); - if (info) { - // We recognize this one. - return info->queue; - } - } - - return nullptr; + transactionsInProgress.Remove(aTransaction); } TransactionThreadPool::TransactionQueue& -TransactionThreadPool::GetQueueForTransaction( - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode) +TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aTransactionId <= mNextTransactionId); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "Null pointer!"); - TransactionQueue* existingQueue = - GetQueueForTransaction(aTransactionId, aDatabaseId); - if (existingQueue) { - return *existingQueue; - } + const nsACString& databaseId = aTransaction->mDatabase->Id(); + + const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; + const uint16_t mode = aTransaction->mMode; // See if we can run this transaction now. DatabaseTransactionInfo* dbTransactionInfo; - if (!mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) { + if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { // First transaction for this database. dbTransactionInfo = new DatabaseTransactionInfo(); - mTransactionsInProgress.Put(aDatabaseId, dbTransactionInfo); + mTransactionsInProgress.Put(databaseId, dbTransactionInfo); } DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = dbTransactionInfo->transactions; - TransactionInfo* info = transactionsInProgress.Get(aTransactionId); + TransactionInfo* info = transactionsInProgress.Get(aTransaction); if (info) { // We recognize this one. return *info->queue; } - TransactionInfo* transactionInfo = new TransactionInfo(this, - aTransactionId, - aDatabaseId, - aObjectStoreNames, - aMode); + TransactionInfo* transactionInfo = new TransactionInfo(aTransaction); - dbTransactionInfo->transactions.Put(aTransactionId, transactionInfo);; + dbTransactionInfo->transactions.Put(aTransaction, transactionInfo);; - for (uint32_t index = 0, count = aObjectStoreNames.Length(); index < count; + for (uint32_t index = 0, count = objectStoreNames.Length(); index < count; index++) { TransactionInfoPair* blockInfo = - dbTransactionInfo->blockingTransactions.Get(aObjectStoreNames[index]); + dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); if (!blockInfo) { blockInfo = new TransactionInfoPair(); blockInfo->lastBlockingReads = nullptr; - dbTransactionInfo->blockingTransactions.Put(aObjectStoreNames[index], + dbTransactionInfo->blockingTransactions.Put(objectStoreNames[index], blockInfo); } @@ -617,7 +332,7 @@ TransactionThreadPool::GetQueueForTransaction( blockingInfo->blocking.PutEntry(transactionInfo); } - if (aMode == IDBTransaction::READ_WRITE && + if (mode == IDBTransaction::READ_WRITE && blockInfo->lastBlockingWrites.Length()) { for (uint32_t index = 0, count = blockInfo->lastBlockingWrites.Length(); index < count; @@ -628,7 +343,7 @@ TransactionThreadPool::GetQueueForTransaction( } } - if (aMode == IDBTransaction::READ_WRITE) { + if (mode == IDBTransaction::READ_WRITE) { blockInfo->lastBlockingReads = transactionInfo; blockInfo->lastBlockingWrites.Clear(); } @@ -644,168 +359,176 @@ TransactionThreadPool::GetQueueForTransaction( return *transactionInfo->queue; } -void -TransactionThreadPool::Dispatch(uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode, +nsresult +TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, nsIRunnable* aRunnable, bool aFinish, - FinishCallback* aFinishCallback) + nsIRunnable* aFinishRunnable) { - MOZ_ASSERT(aTransactionId <= mNextTransactionId); - MOZ_ASSERT(!mShutdownRequested); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "Null pointer!"); + NS_ASSERTION(aRunnable, "Null pointer!"); - TransactionQueue& queue = GetQueueForTransaction(aTransactionId, - aDatabaseId, - aObjectStoreNames, - aMode); + if (aTransaction->mDatabase->IsInvalidated() && !aFinish) { + return NS_ERROR_NOT_AVAILABLE; + } + + TransactionQueue& queue = GetQueueForTransaction(aTransaction); queue.Dispatch(aRunnable); if (aFinish) { - queue.Finish(aFinishCallback); - } -} - -void -TransactionThreadPool::Dispatch(uint64_t aTransactionId, - const nsACString& aDatabaseId, - nsIRunnable* aRunnable, - bool aFinish, - FinishCallback* aFinishCallback) -{ - MOZ_ASSERT(aTransactionId <= mNextTransactionId); - - TransactionQueue* queue = GetQueueForTransaction(aTransactionId, aDatabaseId); - MOZ_ASSERT(queue, "Passed an invalid transaction id!"); - - queue->Dispatch(aRunnable); - if (aFinish) { - queue->Finish(aFinishCallback); + queue.Finish(aFinishRunnable); } + return NS_OK; } void TransactionThreadPool::WaitForDatabasesToComplete( - nsTArray& aDatabaseIds, - nsIRunnable* aCallback) + nsTArray >& aDatabases, + nsIRunnable* aCallback) { - AssertIsOnOwningThread(); - NS_ASSERTION(!aDatabaseIds.IsEmpty(), "No databases to wait on!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!"); NS_ASSERTION(aCallback, "Null pointer!"); - nsAutoPtr callback( - new DatabasesCompleteCallback()); - callback->mCallback = aCallback; - callback->mDatabaseIds.SwapElements(aDatabaseIds); + DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement(); - if (!MaybeFireCallback(callback)) { - mCompleteCallbacks.AppendElement(callback.forget()); + callback->mCallback = aCallback; + callback->mDatabases.SwapElements(aDatabases); + + if (MaybeFireCallback(*callback)) { + mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1); } } // static PLDHashOperator -TransactionThreadPool::CollectTransactions(const uint64_t& aTransactionId, +TransactionThreadPool::CollectTransactions(IDBTransaction* aKey, TransactionInfo* aValue, void* aUserArg) { - nsAutoTArray* transactionArray = - static_cast*>(aUserArg); - transactionArray->AppendElement(aValue); + nsAutoTArray, 50>* transactionArray = + static_cast, 50>*>(aUserArg); + transactionArray->AppendElement(aKey); return PL_DHASH_NEXT; } +void +TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aDatabase, "Null pointer!"); + + PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "AbortTransactionsForDatabase", + js::ProfileEntry::Category::STORAGE); + + // Get list of transactions for this database id + DatabaseTransactionInfo* dbTransactionInfo; + if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) { + // If there are no transactions, we're done. + return; + } + + // Collect any running transactions + DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = + dbTransactionInfo->transactions; + + NS_ASSERTION(transactionsInProgress.Count(), "Should never be 0!"); + + nsAutoTArray, 50> transactions; + transactionsInProgress.EnumerateRead(CollectTransactions, &transactions); + + // Abort transactions. Do this after collecting the transactions in case + // calling Abort() modifies the data structures we're iterating above. + for (uint32_t index = 0; index < transactions.Length(); index++) { + if (transactions[index]->Database() != aDatabase) { + continue; + } + + // This can fail, for example if the transaction is in the process of + // being comitted. That is expected and fine, so we ignore any returned + // errors. + ErrorResult rv; + transactions[index]->Abort(rv); + } +} + struct MOZ_STACK_CLASS TransactionSearchInfo { - explicit TransactionSearchInfo(const nsACString& aDatabaseId) - : databaseId(aDatabaseId) - , found(false) + explicit TransactionSearchInfo(nsIOfflineStorage* aDatabase) + : db(aDatabase), found(false) { } - nsCString databaseId; + nsIOfflineStorage* db; bool found; }; // static PLDHashOperator -TransactionThreadPool::FindTransaction(const uint64_t& aTransactionId, +TransactionThreadPool::FindTransaction(IDBTransaction* aKey, TransactionInfo* aValue, void* aUserArg) { TransactionSearchInfo* info = static_cast(aUserArg); - if (aValue->databaseId == info->databaseId) { + if (aKey->Database() == info->db) { info->found = true; return PL_DHASH_STOP; } return PL_DHASH_NEXT; } - bool -TransactionThreadPool::HasTransactionsForDatabase(const nsACString& aDatabaseId) +TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase) { - AssertIsOnOwningThread(); - MOZ_ASSERT(!aDatabaseId.IsEmpty(), "An empty DatabaseId!"); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aDatabase, "Null pointer!"); DatabaseTransactionInfo* dbTransactionInfo = nullptr; - dbTransactionInfo = mTransactionsInProgress.Get(aDatabaseId); + dbTransactionInfo = mTransactionsInProgress.Get(aDatabase->Id()); if (!dbTransactionInfo) { return false; } - TransactionSearchInfo info(aDatabaseId); + TransactionSearchInfo info(aDatabase); dbTransactionInfo->transactions.EnumerateRead(FindTransaction, &info); return info.found; } bool -TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback) +TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback aCallback) { - AssertIsOnOwningThread(); - MOZ_ASSERT(aCallback); - MOZ_ASSERT(!aCallback->mDatabaseIds.IsEmpty()); - MOZ_ASSERT(aCallback->mCallback); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - PROFILER_LABEL("IndexedDB", - "TransactionThreadPool::MaybeFireCallback", - js::ProfileEntry::Category::STORAGE); + PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "MaybeFireCallback", + js::ProfileEntry::Category::STORAGE); - for (uint32_t count = aCallback->mDatabaseIds.Length(), index = 0; - index < count; - index++) { - const nsCString& databaseId = aCallback->mDatabaseIds[index]; - MOZ_ASSERT(!databaseId.IsEmpty()); + for (uint32_t index = 0; index < aCallback.mDatabases.Length(); index++) { + IDBDatabase* database = aCallback.mDatabases[index]; + if (!database) { + MOZ_CRASH(); + } - if (mTransactionsInProgress.Get(databaseId, nullptr)) { + if (mTransactionsInProgress.Get(database->Id(), nullptr)) { return false; } } - aCallback->mCallback->Run(); + aCallback.mCallback->Run(); return true; } TransactionThreadPool:: -TransactionQueue::TransactionQueue(TransactionThreadPool* aThreadPool, - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode) +TransactionQueue::TransactionQueue(IDBTransaction* aTransaction) : mMonitor("TransactionQueue::mMonitor"), - mOwningThreadPool(aThreadPool), - mTransactionId(aTransactionId), - mDatabaseId(aDatabaseId), - mObjectStoreNames(aObjectStoreNames), - mMode(aMode), + mTransaction(aTransaction), mShouldFinish(false) { - MOZ_ASSERT(aThreadPool); - aThreadPool->AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "Null pointer!"); } void @@ -815,8 +538,8 @@ TransactionThreadPool::TransactionQueue::Unblock() // NB: Finish may be called before Unblock. - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - mOwningThreadPool->mThreadPool->Dispatch(this, NS_DISPATCH_NORMAL))); + TransactionThreadPool::Get()->mThreadPool-> + Dispatch(this, NS_DISPATCH_NORMAL); } void @@ -832,34 +555,35 @@ TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable) } void -TransactionThreadPool::TransactionQueue::Finish(FinishCallback* aFinishCallback) +TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable) { MonitorAutoLock lock(mMonitor); NS_ASSERTION(!mShouldFinish, "Finish called more than once!"); mShouldFinish = true; - mFinishCallback = aFinishCallback; + mFinishRunnable = aFinishRunnable; mMonitor.Notify(); } -NS_IMPL_ISUPPORTS_INHERITED0(TransactionThreadPool::TransactionQueue, - nsRunnable) +NS_IMPL_ISUPPORTS(TransactionThreadPool::TransactionQueue, nsIRunnable) NS_IMETHODIMP TransactionThreadPool::TransactionQueue::Run() { - PROFILER_LABEL("IndexedDB", - "TransactionThreadPool::TransactionQueue""Run", - js::ProfileEntry::Category::STORAGE); + NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + + PROFILER_LABEL("TransactionQueue", "Run", + js::ProfileEntry::Category::STORAGE); IDB_PROFILER_MARK("IndexedDB Transaction %llu: Beginning database work", "IDBTransaction[%llu] DT Start", mTransaction->GetSerialNumber()); nsAutoTArray, 10> queue; - nsRefPtr finishCallback; + nsCOMPtr finishRunnable; bool shouldFinish = false; do { @@ -875,21 +599,13 @@ TransactionThreadPool::TransactionQueue::Run() mQueue.SwapElements(queue); if (mShouldFinish) { - mFinishCallback.swap(finishCallback); + mFinishRunnable.swap(finishRunnable); shouldFinish = true; } } uint32_t count = queue.Length(); for (uint32_t index = 0; index < count; index++) { -#ifdef DEBUG - if (kDEBUGThreadSleepMS) { - MOZ_ALWAYS_TRUE( - PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == - PR_SUCCESS); - } -#endif // DEBUG - nsCOMPtr& runnable = queue[index]; runnable->Run(); runnable = nullptr; @@ -900,84 +616,55 @@ TransactionThreadPool::TransactionQueue::Run() } } while (!shouldFinish); -#ifdef DEBUG - if (kDEBUGThreadSleepMS) { - MOZ_ALWAYS_TRUE( - PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == PR_SUCCESS); - } -#endif // DEBUG - IDB_PROFILER_MARK("IndexedDB Transaction %llu: Finished database work", "IDBTransaction[%llu] DT Done", mTransaction->GetSerialNumber()); - nsRefPtr finishTransactionRunnable = - new FinishTransactionRunnable(mOwningThreadPool.forget(), - mTransactionId, - mDatabaseId, - mObjectStoreNames, - mMode, - finishCallback.forget()); - finishTransactionRunnable->Dispatch(); + nsCOMPtr finishTransactionRunnable = + new FinishTransactionRunnable(mTransaction, finishRunnable); + if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable))) { + NS_WARNING("Failed to dispatch finishTransactionRunnable!"); + } return NS_OK; } -TransactionThreadPool:: FinishTransactionRunnable::FinishTransactionRunnable( - already_AddRefed aThreadPool, - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode, - already_AddRefed aFinishCallback) -: mThreadPool(Move(aThreadPool)), - mFinishCallback(aFinishCallback), - mTransactionId(aTransactionId), - mDatabaseId(aDatabaseId), - mObjectStoreNames(aObjectStoreNames), - mMode(aMode) + IDBTransaction* aTransaction, + nsCOMPtr& aFinishRunnable) +: mTransaction(aTransaction) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aTransaction, "Null pointer!"); + mFinishRunnable.swap(aFinishRunnable); } -NS_IMPL_ISUPPORTS_INHERITED0(TransactionThreadPool::FinishTransactionRunnable, - nsRunnable) +NS_IMPL_ISUPPORTS(FinishTransactionRunnable, nsIRunnable) NS_IMETHODIMP -TransactionThreadPool:: FinishTransactionRunnable::Run() { - MOZ_ASSERT(mThreadPool); - mThreadPool->AssertIsOnOwningThread(); + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - PROFILER_LABEL("IndexedDB", - "TransactionThreadPool::FinishTransactionRunnable::Run", - js::ProfileEntry::Category::STORAGE); + PROFILER_MAIN_THREAD_LABEL("FinishTransactionRunnable", "Run", + js::ProfileEntry::Category::STORAGE); - nsRefPtr threadPool; - mThreadPool.swap(threadPool); - - nsRefPtr callback; - mFinishCallback.swap(callback); - - if (callback) { - callback->TransactionFinishedBeforeUnblock(); + if (!gThreadPool) { + NS_ERROR("Running after shutdown!"); + return NS_ERROR_FAILURE; } - threadPool->FinishTransaction(mTransactionId, - mDatabaseId, - mObjectStoreNames, - mMode); + gThreadPool->FinishTransaction(mTransaction); - if (callback) { - callback->TransactionFinishedAfterUnblock(); + if (mFinishRunnable) { + mFinishRunnable->Run(); + mFinishRunnable = nullptr; } return NS_OK; } -#ifdef BUILD_THREADPOOL_LISTENER +#ifdef MOZ_ENABLE_PROFILER_SPS NS_IMPL_ISUPPORTS(TransactionThreadPoolListener, nsIThreadPoolListener) @@ -985,24 +672,8 @@ NS_IMETHODIMP TransactionThreadPoolListener::OnThreadCreated() { MOZ_ASSERT(!NS_IsMainThread()); - -#ifdef MOZ_ENABLE_PROFILER_SPS char aLocal; profiler_register_thread("IndexedDB Transaction", &aLocal); -#endif // MOZ_ENABLE_PROFILER_SPS - -#ifdef DEBUG - if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { - NS_WARNING("TransactionThreadPool thread debugging enabled, priority has " - "been modified!"); - nsCOMPtr thread = - do_QueryInterface(NS_GetCurrentThread()); - MOZ_ASSERT(thread); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->SetPriority(kDEBUGThreadPriority))); - } -#endif // DEBUG - return NS_OK; } @@ -1010,16 +681,8 @@ NS_IMETHODIMP TransactionThreadPoolListener::OnThreadShuttingDown() { MOZ_ASSERT(!NS_IsMainThread()); - -#ifdef MOZ_ENABLE_PROFILER_SPS profiler_unregister_thread(); -#endif // MOZ_ENABLE_PROFILER_SPS - return NS_OK; } -#endif // BUILD_THREADPOOL_LISTENER - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +#endif // MOZ_ENABLE_PROFILER_SPS diff --git a/dom/indexedDB/TransactionThreadPool.h b/dom/indexedDB/TransactionThreadPool.h index 7ed275928e28..4e90cc186fda 100644 --- a/dom/indexedDB/TransactionThreadPool.h +++ b/dom/indexedDB/TransactionThreadPool.h @@ -7,96 +7,145 @@ #ifndef mozilla_dom_indexeddb_transactionthreadpool_h__ #define mozilla_dom_indexeddb_transactionthreadpool_h__ -#include "mozilla/Attributes.h" -#include "nsAutoPtr.h" -#include "nsClassHashtable.h" -#include "nsCOMPtr.h" -#include "nsHashKeys.h" -#include "nsISupportsImpl.h" -#include "nsTArray.h" +// Only meant to be included in IndexedDB source files, not exported. +#include "IndexedDatabase.h" + +#include "nsIObserver.h" +#include "nsIRunnable.h" + +#include "mozilla/Monitor.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" + +#include "IDBTransaction.h" -class nsIEventTarget; -class nsIRunnable; class nsIThreadPool; -namespace mozilla { -namespace dom { -namespace indexedDB { +BEGIN_INDEXEDDB_NAMESPACE -class TransactionThreadPool MOZ_FINAL +class FinishTransactionRunnable; +class QueuedDispatchInfo; + +class TransactionThreadPool { - class FinishTransactionRunnable; + friend class nsAutoPtr; friend class FinishTransactionRunnable; - class TransactionQueue; - friend class TransactionQueue; - - struct DatabaseTransactionInfo; - struct DatabasesCompleteCallback; - struct TransactionInfo; - struct TransactionInfoPair; - - nsCOMPtr mThreadPool; - nsCOMPtr mOwningThread; - - nsClassHashtable - mTransactionsInProgress; - - nsTArray> mCompleteCallbacks; - - uint64_t mNextTransactionId; - bool mShutdownRequested; - bool mShutdownComplete; - public: - class FinishCallback; + // returns a non-owning ref! + static TransactionThreadPool* GetOrCreate(); - static already_AddRefed Create(); + // returns a non-owning ref! + static TransactionThreadPool* Get(); - uint64_t NextTransactionId(); + static void Shutdown(); - void Dispatch(uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode, - nsIRunnable* aRunnable, - bool aFinish, - FinishCallback* aFinishCallback); + nsresult Dispatch(IDBTransaction* aTransaction, + nsIRunnable* aRunnable, + bool aFinish, + nsIRunnable* aFinishRunnable); - void Dispatch(uint64_t aTransactionId, - const nsACString& aDatabaseId, - nsIRunnable* aRunnable, - bool aFinish, - FinishCallback* aFinishCallback); - - void WaitForDatabasesToComplete(nsTArray& aDatabaseIds, + void WaitForDatabasesToComplete(nsTArray >& aDatabases, nsIRunnable* aCallback); + // Abort all transactions, unless they are already in the process of being + // committed, for aDatabase. + void AbortTransactionsForDatabase(IDBDatabase* aDatabase); + // Returns true if there are running or pending transactions for aDatabase. - bool HasTransactionsForDatabase(const nsACString& aDatabaseId); + bool HasTransactionsForDatabase(IDBDatabase* aDatabase); - NS_INLINE_DECL_REFCOUNTING(TransactionThreadPool) +protected: + class TransactionQueue MOZ_FINAL : public nsIRunnable + { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIRUNNABLE - void ShutdownAndSpin(); - void ShutdownAsync(); + explicit TransactionQueue(IDBTransaction* aTransaction); - bool HasCompletedShutdown() const; + void Unblock(); - void AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else - { } -#endif + void Dispatch(nsIRunnable* aRunnable); + + void Finish(nsIRunnable* aFinishRunnable); + + private: + ~TransactionQueue() {} + + mozilla::Monitor mMonitor; + IDBTransaction* mTransaction; + nsAutoTArray, 10> mQueue; + nsCOMPtr mFinishRunnable; + bool mShouldFinish; + }; + + friend class TransactionQueue; + + struct TransactionInfo + { + explicit TransactionInfo(IDBTransaction* aTransaction) + { + MOZ_COUNT_CTOR(TransactionInfo); + + transaction = aTransaction; + queue = new TransactionQueue(aTransaction); + } + + ~TransactionInfo() + { + MOZ_COUNT_DTOR(TransactionInfo); + } + + nsRefPtr transaction; + nsRefPtr queue; + nsTHashtable > blockedOn; + nsTHashtable > blocking; + }; + + struct TransactionInfoPair + { + TransactionInfoPair() + : lastBlockingReads(nullptr) + { + MOZ_COUNT_CTOR(TransactionInfoPair); + } + + ~TransactionInfoPair() + { + MOZ_COUNT_DTOR(TransactionInfoPair); + } + // Multiple reading transactions can block future writes. + nsTArray lastBlockingWrites; + // But only a single writing transaction can block future reads. + TransactionInfo* lastBlockingReads; + }; + + struct DatabaseTransactionInfo + { + DatabaseTransactionInfo() + { + MOZ_COUNT_CTOR(DatabaseTransactionInfo); + } + + ~DatabaseTransactionInfo() + { + MOZ_COUNT_DTOR(DatabaseTransactionInfo); + } + + typedef nsClassHashtable, TransactionInfo > + TransactionHashtable; + TransactionHashtable transactions; + nsClassHashtable blockingTransactions; + }; -private: static PLDHashOperator - CollectTransactions(const uint64_t& aTransactionId, + CollectTransactions(IDBTransaction* aKey, TransactionInfo* aValue, void* aUserArg); static PLDHashOperator - FindTransaction(const uint64_t& aTransactionId, + FindTransaction(IDBTransaction* aKey, TransactionInfo* aValue, void* aUserArg); @@ -104,59 +153,32 @@ private: MaybeUnblockTransaction(nsPtrHashKey* aKey, void* aUserArg); - TransactionThreadPool(); + struct DatabasesCompleteCallback + { + nsTArray > mDatabases; + nsCOMPtr mCallback; + }; - // Reference counted. + TransactionThreadPool(); ~TransactionThreadPool(); nsresult Init(); - void Cleanup(); + nsresult Cleanup(); - void FinishTransaction(uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode); + void FinishTransaction(IDBTransaction* aTransaction); - TransactionQueue* GetQueueForTransaction(uint64_t aTransactionId, - const nsACString& aDatabaseId); + TransactionQueue& GetQueueForTransaction(IDBTransaction* aTransaction); - TransactionQueue& GetQueueForTransaction( - uint64_t aTransactionId, - const nsACString& aDatabaseId, - const nsTArray& aObjectStoreNames, - uint16_t aMode); + bool MaybeFireCallback(DatabasesCompleteCallback aCallback); - bool MaybeFireCallback(DatabasesCompleteCallback* aCallback); + nsCOMPtr mThreadPool; - void CleanupAsync(); + nsClassHashtable + mTransactionsInProgress; + + nsTArray mCompleteCallbacks; }; -class NS_NO_VTABLE TransactionThreadPool::FinishCallback -{ -public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() = 0; - - NS_IMETHOD_(MozExternalRefCountType) - Release() = 0; - - // Called on the owning thread before any additional transactions are - // unblocked. - virtual void - TransactionFinishedBeforeUnblock() = 0; - - // Called on the owning thread after additional transactions may have been - // unblocked. - virtual void - TransactionFinishedAfterUnblock() = 0; - -protected: - FinishCallback() - { } -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla +END_INDEXEDDB_NAMESPACE #endif // mozilla_dom_indexeddb_transactionthreadpool_h__ diff --git a/dom/indexedDB/ipc/IndexedDBChild.cpp b/dom/indexedDB/ipc/IndexedDBChild.cpp new file mode 100644 index 000000000000..5f2754cd9ce5 --- /dev/null +++ b/dom/indexedDB/ipc/IndexedDBChild.cpp @@ -0,0 +1,1401 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/basictypes.h" + +#include "IndexedDBChild.h" + +#include "nsIInputStream.h" + +#include "mozilla/Assertions.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/quota/Client.h" +#include "mozilla/dom/quota/QuotaManager.h" + +#include "AsyncConnectionHelper.h" +#include "DatabaseInfo.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IDBIndex.h" +#include "IDBObjectStore.h" +#include "IDBTransaction.h" + +USING_INDEXEDDB_NAMESPACE + +using namespace mozilla::dom; +using mozilla::dom::quota::Client; +using mozilla::dom::quota::QuotaManager; + +namespace { + +class IPCOpenDatabaseHelper : public AsyncConnectionHelper +{ +public: + IPCOpenDatabaseHelper(IDBDatabase* aDatabase, IDBOpenDBRequest* aRequest) + : AsyncConnectionHelper(aDatabase, aRequest) + { + MOZ_ASSERT(aRequest); + } + + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual nsresult + OnSuccess() MOZ_OVERRIDE + { + static_cast(mRequest.get())->SetTransaction(nullptr); + return AsyncConnectionHelper::OnSuccess(); + } + + virtual void + OnError() MOZ_OVERRIDE + { + static_cast(mRequest.get())->SetTransaction(nullptr); + AsyncConnectionHelper::OnError(); + } + + virtual nsresult + DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; +}; + +class IPCSetVersionHelper : public AsyncConnectionHelper +{ + nsRefPtr mOpenRequest; + uint64_t mOldVersion; + uint64_t mRequestedVersion; + +public: + IPCSetVersionHelper(IDBTransaction* aTransaction, IDBOpenDBRequest* aRequest, + uint64_t aOldVersion, uint64_t aRequestedVersion) + : AsyncConnectionHelper(aTransaction, aRequest), + mOpenRequest(aRequest), mOldVersion(aOldVersion), + mRequestedVersion(aRequestedVersion) + { + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(aRequest); + } + + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; + + virtual already_AddRefed + CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) MOZ_OVERRIDE; + + virtual nsresult + GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) MOZ_OVERRIDE; +}; + +class IPCDeleteDatabaseHelper : public AsyncConnectionHelper +{ +public: + explicit IPCDeleteDatabaseHelper(IDBRequest* aRequest) + : AsyncConnectionHelper(static_cast(nullptr), aRequest) + { } + + virtual nsresult UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) + MOZ_OVERRIDE; + + virtual ChildProcessSendResult + SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; + + virtual nsresult + GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) MOZ_OVERRIDE; + + virtual nsresult + DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; +}; + +class VersionChangeRunnable : public nsRunnable +{ + nsRefPtr mDatabase; + uint64_t mOldVersion; + uint64_t mNewVersion; + +public: + VersionChangeRunnable(IDBDatabase* aDatabase, const uint64_t& aOldVersion, + const uint64_t& aNewVersion) + : mDatabase(aDatabase), mOldVersion(aOldVersion), mNewVersion(aNewVersion) + { + MOZ_ASSERT(aDatabase); + } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + if (mDatabase->IsClosed()) { + return NS_OK; + } + + nsRefPtr event = + IDBVersionChangeEvent::Create(mDatabase, mOldVersion, mNewVersion); + MOZ_ASSERT(event); + + bool dummy; + nsresult rv = mDatabase->DispatchEvent(event, &dummy); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } +}; + +} // anonymous namespace + +/******************************************************************************* + * IndexedDBChild + ******************************************************************************/ + +IndexedDBChild::IndexedDBChild(ContentChild* aContentChild, + const nsCString& aASCIIOrigin) +: mFactory(nullptr) +, mManagerContent(aContentChild) +, mManagerTab(nullptr) +, mASCIIOrigin(aASCIIOrigin) +#ifdef DEBUG +, mDisconnected(false) +#endif +{ + MOZ_ASSERT(aContentChild); + MOZ_COUNT_CTOR(IndexedDBChild); +} + +IndexedDBChild::IndexedDBChild(TabChild* aTabChild, + const nsCString& aASCIIOrigin) +: mFactory(nullptr) +, mManagerContent(nullptr) +, mManagerTab(aTabChild) +, mASCIIOrigin(aASCIIOrigin) +#ifdef DEBUG +, mDisconnected(false) +#endif +{ + MOZ_ASSERT(aTabChild); + MOZ_COUNT_CTOR(IndexedDBChild); +} + +IndexedDBChild::~IndexedDBChild() +{ + MOZ_COUNT_DTOR(IndexedDBChild); + MOZ_ASSERT(!mFactory); +} + +void +IndexedDBChild::SetFactory(IDBFactory* aFactory) +{ + MOZ_ASSERT(aFactory); + MOZ_ASSERT(!mFactory); + + aFactory->SetActor(this); + mFactory = aFactory; +} + +void +IndexedDBChild::Disconnect() +{ +#ifdef DEBUG + MOZ_ASSERT(!mDisconnected); + mDisconnected = true; +#endif + + const InfallibleTArray& databases = + ManagedPIndexedDBDatabaseChild(); + for (uint32_t i = 0; i < databases.Length(); ++i) { + static_cast(databases[i])->Disconnect(); + } +} + +void +IndexedDBChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mFactory) { + mFactory->SetActor(static_cast(nullptr)); +#ifdef DEBUG + mFactory = nullptr; +#endif + } +} + +PIndexedDBDatabaseChild* +IndexedDBChild::AllocPIndexedDBDatabaseChild( + const nsString& aName, + const uint64_t& aVersion, + const PersistenceType& aPersistenceType) +{ + return new IndexedDBDatabaseChild(aName, aVersion); +} + +bool +IndexedDBChild::DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBDeleteDatabaseRequestChild* +IndexedDBChild::AllocPIndexedDBDeleteDatabaseRequestChild( + const nsString& aName, + const PersistenceType& aPersistenceType) +{ + MOZ_CRASH("Caller is supposed to manually construct a request!"); +} + +bool +IndexedDBChild::DeallocPIndexedDBDeleteDatabaseRequestChild( + PIndexedDBDeleteDatabaseRequestChild* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBDatabaseChild + ******************************************************************************/ + +IndexedDBDatabaseChild::IndexedDBDatabaseChild(const nsString& aName, + uint64_t aVersion) +: mDatabase(nullptr), mName(aName), mVersion(aVersion) +{ + MOZ_COUNT_CTOR(IndexedDBDatabaseChild); +} + +IndexedDBDatabaseChild::~IndexedDBDatabaseChild() +{ + MOZ_COUNT_DTOR(IndexedDBDatabaseChild); + MOZ_ASSERT(!mDatabase); + MOZ_ASSERT(!mStrongDatabase); +} + +void +IndexedDBDatabaseChild::SetRequest(IDBOpenDBRequest* aRequest) +{ + MOZ_ASSERT(aRequest); + MOZ_ASSERT(!mRequest); + + mRequest = aRequest; +} + +void +IndexedDBDatabaseChild::Disconnect() +{ + const InfallibleTArray& transactions = + ManagedPIndexedDBTransactionChild(); + for (uint32_t i = 0; i < transactions.Length(); ++i) { + static_cast(transactions[i])->Disconnect(); + } +} + +bool +IndexedDBDatabaseChild::EnsureDatabase( + IDBOpenDBRequest* aRequest, + const DatabaseInfoGuts& aDBInfo, + const InfallibleTArray& aOSInfo) +{ + nsCString databaseId; + if (mDatabase) { + databaseId = mDatabase->Id(); + } + else { + QuotaManager::GetStorageId(aDBInfo.persistenceType, aDBInfo.origin, + Client::IDB, aDBInfo.name, databaseId); + } + MOZ_ASSERT(!databaseId.IsEmpty()); + + nsRefPtr dbInfo; + if (DatabaseInfo::Get(databaseId, getter_AddRefs(dbInfo))) { + dbInfo->version = aDBInfo.version; + } + else { + nsRefPtr newInfo = new DatabaseInfo(); + + *static_cast(newInfo.get()) = aDBInfo; + newInfo->id = databaseId; + + if (!DatabaseInfo::Put(newInfo)) { + NS_WARNING("Out of memory!"); + return false; + } + + newInfo.swap(dbInfo); + + // This is more or less copied from IDBFactory::SetDatabaseMetadata. + for (uint32_t i = 0; i < aOSInfo.Length(); i++) { + nsRefPtr newInfo = new ObjectStoreInfo(); + *static_cast(newInfo.get()) = aOSInfo[i]; + + if (!dbInfo->PutObjectStore(newInfo)) { + NS_WARNING("Out of memory!"); + return false; + } + } + } + + if (!mDatabase) { + nsRefPtr database = + IDBDatabase::Create(aRequest, aRequest->Factory(), dbInfo.forget(), + aDBInfo.origin, nullptr, nullptr); + if (!database) { + NS_WARNING("Failed to create database!"); + return false; + } + + database->SetActor(this); + + mDatabase = database; + mStrongDatabase = database.forget(); + } + + return true; +} + +void +IndexedDBDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mDatabase) { + mDatabase->SetActor(static_cast(nullptr)); +#ifdef DEBUG + mDatabase = nullptr; +#endif + } +} + +bool +IndexedDBDatabaseChild::RecvSuccess( + const DatabaseInfoGuts& aDBInfo, + const InfallibleTArray& aOSInfo) +{ +#ifdef DEBUG + { + IndexedDBChild* manager = static_cast(Manager()); + MOZ_ASSERT(aDBInfo.origin == manager->ASCIIOrigin()); + MOZ_ASSERT(aDBInfo.name == mName); + MOZ_ASSERT(!mVersion || aDBInfo.version == mVersion); + } +#endif + + MOZ_ASSERT(mRequest); + + nsRefPtr request; + mRequest.swap(request); + + nsRefPtr openHelper; + mOpenHelper.swap(openHelper); + + if (!EnsureDatabase(request, aDBInfo, aOSInfo)) { + return false; + } + + MOZ_ASSERT(mStrongDatabase); + nsRefPtr database; + mStrongDatabase.swap(database); + + if (openHelper) { + request->Reset(); + } + else { + openHelper = new IPCOpenDatabaseHelper(mDatabase, request); + } + + ImmediateRunEventTarget target; + if (NS_FAILED(openHelper->Dispatch(&target))) { + NS_WARNING("Dispatch of IPCOpenDatabaseHelper failed!"); + return false; + } + + return true; +} + +bool +IndexedDBDatabaseChild::RecvError(const nsresult& aRv) +{ + MOZ_ASSERT(mRequest); + + nsRefPtr request; + mRequest.swap(request); + + nsRefPtr database; + mStrongDatabase.swap(database); + + nsRefPtr openHelper; + mOpenHelper.swap(openHelper); + + if (openHelper) { + request->Reset(); + } + else { + openHelper = new IPCOpenDatabaseHelper(nullptr, request); + } + + openHelper->SetError(aRv); + + ImmediateRunEventTarget target; + if (NS_FAILED(openHelper->Dispatch(&target))) { + NS_WARNING("Dispatch of IPCOpenDatabaseHelper failed!"); + return false; + } + + return true; +} + +bool +IndexedDBDatabaseChild::RecvBlocked(const uint64_t& aOldVersion) +{ + MOZ_ASSERT(mRequest); + MOZ_ASSERT(!mDatabase); + + nsCOMPtr runnable = + IDBVersionChangeEvent::CreateBlockedRunnable(mRequest, aOldVersion, mVersion); + + ImmediateRunEventTarget target; + if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) { + NS_WARNING("Dispatch of blocked event failed!"); + } + + return true; +} + +bool +IndexedDBDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, + const uint64_t& aNewVersion) +{ + MOZ_ASSERT(mDatabase); + + nsCOMPtr runnable = + new VersionChangeRunnable(mDatabase, aOldVersion, aNewVersion); + + ImmediateRunEventTarget target; + if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) { + NS_WARNING("Dispatch of versionchange event failed!"); + } + + return true; +} + +bool +IndexedDBDatabaseChild::RecvInvalidate() +{ + if (mDatabase) { + mDatabase->Invalidate(); + } + return true; +} + +bool +IndexedDBDatabaseChild::RecvPIndexedDBTransactionConstructor( + PIndexedDBTransactionChild* aActor, + const TransactionParams& aParams) +{ + // This only happens when the parent has created a version-change transaction + // for us. + + IndexedDBTransactionChild* actor = + static_cast(aActor); + MOZ_ASSERT(!actor->GetTransaction()); + + MOZ_ASSERT(aParams.type() == + TransactionParams::TVersionChangeTransactionParams); + + const VersionChangeTransactionParams& params = + aParams.get_VersionChangeTransactionParams(); + + const DatabaseInfoGuts& dbInfo = params.dbInfo(); + const InfallibleTArray& osInfo = params.osInfo(); + uint64_t oldVersion = params.oldVersion(); + + MOZ_ASSERT(dbInfo.origin == + static_cast(Manager())->ASCIIOrigin()); + MOZ_ASSERT(dbInfo.name == mName); + MOZ_ASSERT(!mVersion || dbInfo.version == mVersion); + MOZ_ASSERT(!mVersion || oldVersion < mVersion); + + MOZ_ASSERT(mRequest); + MOZ_ASSERT(!mDatabase); + MOZ_ASSERT(!mOpenHelper); + + if (!EnsureDatabase(mRequest, dbInfo, osInfo)) { + return false; + } + + nsRefPtr helper = + new IPCOpenDatabaseHelper(mDatabase, mRequest); + + Sequence storesToOpen; + nsRefPtr transaction = + IDBTransaction::CreateInternal(mDatabase, storesToOpen, + IDBTransaction::VERSION_CHANGE, false, true); + NS_ENSURE_TRUE(transaction, false); + + nsRefPtr versionHelper = + new IPCSetVersionHelper(transaction, mRequest, oldVersion, mVersion); + + mDatabase->EnterSetVersionTransaction(); + mDatabase->mPreviousDatabaseInfo->version = oldVersion; + + actor->SetTransaction(transaction); + + ImmediateRunEventTarget target; + if (NS_FAILED(versionHelper->Dispatch(&target))) { + NS_WARNING("Dispatch of IPCSetVersionHelper failed!"); + return false; + } + + mOpenHelper = helper.forget(); + return true; +} + +PIndexedDBTransactionChild* +IndexedDBDatabaseChild::AllocPIndexedDBTransactionChild( + const TransactionParams& aParams) +{ + MOZ_ASSERT(aParams.type() == + TransactionParams::TVersionChangeTransactionParams); + return new IndexedDBTransactionChild(); +} + +bool +IndexedDBDatabaseChild::DeallocPIndexedDBTransactionChild( + PIndexedDBTransactionChild* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBTransactionChild + ******************************************************************************/ + +IndexedDBTransactionChild::IndexedDBTransactionChild() +: mTransaction(nullptr) +{ + MOZ_COUNT_CTOR(IndexedDBTransactionChild); +} + +IndexedDBTransactionChild::~IndexedDBTransactionChild() +{ + MOZ_COUNT_DTOR(IndexedDBTransactionChild); + MOZ_ASSERT(!mTransaction); + MOZ_ASSERT(!mStrongTransaction); +} + +void +IndexedDBTransactionChild::SetTransaction(IDBTransaction* aTransaction) +{ + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(!mTransaction); + + aTransaction->SetActor(this); + + mTransaction = aTransaction; + mStrongTransaction = aTransaction; +} + +void +IndexedDBTransactionChild::Disconnect() +{ + const InfallibleTArray& objectStores = + ManagedPIndexedDBObjectStoreChild(); + for (uint32_t i = 0; i < objectStores.Length(); ++i) { + static_cast(objectStores[i])->Disconnect(); + } +} + +void +IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv) +{ + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mStrongTransaction); + + nsRefPtr transaction; + mStrongTransaction.swap(transaction); + + if (transaction->GetMode() == IDBTransaction::VERSION_CHANGE) { + transaction->Database()->ExitSetVersionTransaction(); + } + + nsRefPtr helper = new CommitHelper(transaction, aRv); + + ImmediateRunEventTarget target; + if (NS_FAILED(target.Dispatch(helper, NS_DISPATCH_NORMAL))) { + NS_WARNING("Dispatch of CommitHelper failed!"); + } +} + +void +IndexedDBTransactionChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mStrongTransaction) { + // We're being torn down before we received a complete event from the parent + // so fake one here. + FireCompleteEvent(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + MOZ_ASSERT(!mStrongTransaction); + } + + if (mTransaction) { + mTransaction->SetActor(static_cast(nullptr)); +#ifdef DEBUG + mTransaction = nullptr; +#endif + } +} + +bool +IndexedDBTransactionChild::RecvComplete(const CompleteParams& aParams) +{ + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mStrongTransaction); + + nsresult resultCode; + + switch (aParams.type()) { + case CompleteParams::TCompleteResult: + resultCode = NS_OK; + break; + case CompleteParams::TAbortResult: + resultCode = aParams.get_AbortResult().errorCode(); + if (NS_SUCCEEDED(resultCode)) { + resultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; + } + break; + + default: + MOZ_CRASH("Unknown union type!"); + } + + FireCompleteEvent(resultCode); + return true; +} + +PIndexedDBObjectStoreChild* +IndexedDBTransactionChild::AllocPIndexedDBObjectStoreChild( + const ObjectStoreConstructorParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct an object store!"); +} + +bool +IndexedDBTransactionChild::DeallocPIndexedDBObjectStoreChild( + PIndexedDBObjectStoreChild* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBObjectStoreChild + ******************************************************************************/ + +IndexedDBObjectStoreChild::IndexedDBObjectStoreChild( + IDBObjectStore* aObjectStore) +: mObjectStore(aObjectStore) +{ + MOZ_COUNT_CTOR(IndexedDBObjectStoreChild); + aObjectStore->SetActor(this); +} + +IndexedDBObjectStoreChild::~IndexedDBObjectStoreChild() +{ + MOZ_COUNT_DTOR(IndexedDBObjectStoreChild); + MOZ_ASSERT(!mObjectStore); +} + +void +IndexedDBObjectStoreChild::Disconnect() +{ + const InfallibleTArray& requests = + ManagedPIndexedDBRequestChild(); + for (uint32_t i = 0; i < requests.Length(); ++i) { + static_cast(requests[i])->Disconnect(); + } + + const InfallibleTArray& indexes = + ManagedPIndexedDBIndexChild(); + for (uint32_t i = 0; i < indexes.Length(); ++i) { + static_cast(indexes[i])->Disconnect(); + } + + const InfallibleTArray& cursors = + ManagedPIndexedDBCursorChild(); + for (uint32_t i = 0; i < cursors.Length(); ++i) { + static_cast(cursors[i])->Disconnect(); + } +} + +void +IndexedDBObjectStoreChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mObjectStore) { + mObjectStore->SetActor(static_cast(nullptr)); +#ifdef DEBUG + mObjectStore = nullptr; +#endif + } +} + +bool +IndexedDBObjectStoreChild::RecvPIndexedDBCursorConstructor( + PIndexedDBCursorChild* aActor, + const ObjectStoreCursorConstructorParams& aParams) +{ + IndexedDBCursorChild* actor = static_cast(aActor); + + IndexedDBObjectStoreRequestChild* requestActor = + static_cast(aParams.requestChild()); + NS_ASSERTION(requestActor, "Must have an actor here!"); + + nsRefPtr request = requestActor->GetRequest(); + NS_ASSERTION(request, "Must have a request here!"); + + size_t direction = static_cast(aParams.direction()); + + nsRefPtr cursor; + nsresult rv; + + typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType; + + switch (aParams.optionalCloneInfo().type()) { + case CursorUnionType::TSerializedStructuredCloneReadInfo: { + nsTArray blobs; + IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs); + + const SerializedStructuredCloneReadInfo& cloneInfo = + aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo(); + + rv = mObjectStore->OpenCursorFromChildProcess(request, direction, + aParams.key(), cloneInfo, + blobs, + getter_AddRefs(cursor)); + NS_ENSURE_SUCCESS(rv, false); + + MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!"); + } break; + + case CursorUnionType::Tvoid_t: + MOZ_ASSERT(aParams.blobsChild().IsEmpty()); + + rv = mObjectStore->OpenCursorFromChildProcess(request, direction, + aParams.key(), + getter_AddRefs(cursor)); + NS_ENSURE_SUCCESS(rv, false); + break; + + default: + MOZ_CRASH("Unknown union type!"); + } + + actor->SetCursor(cursor); + return true; +} + +PIndexedDBRequestChild* +IndexedDBObjectStoreChild::AllocPIndexedDBRequestChild( + const ObjectStoreRequestParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct a request!"); +} + +bool +IndexedDBObjectStoreChild::DeallocPIndexedDBRequestChild( + PIndexedDBRequestChild* aActor) +{ + delete aActor; + return false; +} + +PIndexedDBIndexChild* +IndexedDBObjectStoreChild::AllocPIndexedDBIndexChild( + const IndexConstructorParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct an index!"); +} + +bool +IndexedDBObjectStoreChild::DeallocPIndexedDBIndexChild(PIndexedDBIndexChild* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBCursorChild* +IndexedDBObjectStoreChild::AllocPIndexedDBCursorChild( + const ObjectStoreCursorConstructorParams& aParams) +{ + return new IndexedDBCursorChild(); +} + +bool +IndexedDBObjectStoreChild::DeallocPIndexedDBCursorChild( + PIndexedDBCursorChild* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBIndexChild + ******************************************************************************/ + +IndexedDBIndexChild::IndexedDBIndexChild(IDBIndex* aIndex) +: mIndex(aIndex) +{ + MOZ_COUNT_CTOR(IndexedDBIndexChild); + aIndex->SetActor(this); +} + +IndexedDBIndexChild::~IndexedDBIndexChild() +{ + MOZ_COUNT_DTOR(IndexedDBIndexChild); + MOZ_ASSERT(!mIndex); +} + +void +IndexedDBIndexChild::Disconnect() +{ + const InfallibleTArray& requests = + ManagedPIndexedDBRequestChild(); + for (uint32_t i = 0; i < requests.Length(); ++i) { + static_cast(requests[i])->Disconnect(); + } + + const InfallibleTArray& cursors = + ManagedPIndexedDBCursorChild(); + for (uint32_t i = 0; i < cursors.Length(); ++i) { + static_cast(cursors[i])->Disconnect(); + } +} + +void +IndexedDBIndexChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mIndex) { + mIndex->SetActor(static_cast(nullptr)); +#ifdef DEBUG + mIndex = nullptr; +#endif + } +} + +bool +IndexedDBIndexChild::RecvPIndexedDBCursorConstructor( + PIndexedDBCursorChild* aActor, + const IndexCursorConstructorParams& aParams) +{ + IndexedDBCursorChild* actor = static_cast(aActor); + + IndexedDBObjectStoreRequestChild* requestActor = + static_cast(aParams.requestChild()); + NS_ASSERTION(requestActor, "Must have an actor here!"); + + nsRefPtr request = requestActor->GetRequest(); + NS_ASSERTION(request, "Must have a request here!"); + + size_t direction = static_cast(aParams.direction()); + + nsRefPtr cursor; + nsresult rv; + + typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType; + + switch (aParams.optionalCloneInfo().type()) { + case CursorUnionType::TSerializedStructuredCloneReadInfo: { + nsTArray blobs; + IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs); + + const SerializedStructuredCloneReadInfo& cloneInfo = + aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo(); + + rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(), + aParams.objectKey(), cloneInfo, + blobs, + getter_AddRefs(cursor)); + NS_ENSURE_SUCCESS(rv, false); + } break; + + case CursorUnionType::Tvoid_t: + MOZ_ASSERT(aParams.blobsChild().IsEmpty()); + + rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(), + aParams.objectKey(), + getter_AddRefs(cursor)); + NS_ENSURE_SUCCESS(rv, false); + break; + + default: + MOZ_CRASH("Unknown union type!"); + } + + actor->SetCursor(cursor); + return true; +} + +PIndexedDBRequestChild* +IndexedDBIndexChild::AllocPIndexedDBRequestChild(const IndexRequestParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct a request!"); +} + +bool +IndexedDBIndexChild::DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBCursorChild* +IndexedDBIndexChild::AllocPIndexedDBCursorChild( + const IndexCursorConstructorParams& aParams) +{ + return new IndexedDBCursorChild(); +} + +bool +IndexedDBIndexChild::DeallocPIndexedDBCursorChild(PIndexedDBCursorChild* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBCursorChild + ******************************************************************************/ + +IndexedDBCursorChild::IndexedDBCursorChild() +: mCursor(nullptr) +{ + MOZ_COUNT_CTOR(IndexedDBCursorChild); +} + +IndexedDBCursorChild::~IndexedDBCursorChild() +{ + MOZ_COUNT_DTOR(IndexedDBCursorChild); + MOZ_ASSERT(!mCursor); + MOZ_ASSERT(!mStrongCursor); +} + +void +IndexedDBCursorChild::SetCursor(IDBCursor* aCursor) +{ + MOZ_ASSERT(aCursor); + MOZ_ASSERT(!mCursor); + + aCursor->SetActor(this); + + mCursor = aCursor; + mStrongCursor = aCursor; +} + +void +IndexedDBCursorChild::Disconnect() +{ + const InfallibleTArray& requests = + ManagedPIndexedDBRequestChild(); + for (uint32_t i = 0; i < requests.Length(); ++i) { + static_cast(requests[i])->Disconnect(); + } +} + +void +IndexedDBCursorChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mCursor) { + mCursor->SetActor(static_cast(nullptr)); +#ifdef DEBUG + mCursor = nullptr; +#endif + } +} + +PIndexedDBRequestChild* +IndexedDBCursorChild::AllocPIndexedDBRequestChild(const CursorRequestParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct a request!"); +} + +bool +IndexedDBCursorChild::DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBRequestChildBase + ******************************************************************************/ + +IndexedDBRequestChildBase::IndexedDBRequestChildBase( + AsyncConnectionHelper* aHelper) +: mHelper(aHelper) +{ + MOZ_COUNT_CTOR(IndexedDBRequestChildBase); +} + +IndexedDBRequestChildBase::~IndexedDBRequestChildBase() +{ + MOZ_COUNT_DTOR(IndexedDBRequestChildBase); +} + +IDBRequest* +IndexedDBRequestChildBase::GetRequest() const +{ + return mHelper ? mHelper->GetRequest() : nullptr; +} + +void +IndexedDBRequestChildBase::Disconnect() +{ + if (mHelper) { + IDBRequest* request = mHelper->GetRequest(); + + if (request->IsPending()) { + request->SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + IDBTransaction* transaction = mHelper->GetTransaction(); + if (transaction) { + transaction->OnRequestDisconnected(); + } + } + } +} + +bool +IndexedDBRequestChildBase::Recv__delete__(const ResponseValue& aResponse) +{ + MOZ_CRASH("This should be overridden!"); +} + +/******************************************************************************* + * IndexedDBObjectStoreRequestChild + ******************************************************************************/ + +IndexedDBObjectStoreRequestChild::IndexedDBObjectStoreRequestChild( + AsyncConnectionHelper* aHelper, + IDBObjectStore* aObjectStore, + RequestType aRequestType) +: IndexedDBRequestChildBase(aHelper), mObjectStore(aObjectStore), + mRequestType(aRequestType) +{ + MOZ_COUNT_CTOR(IndexedDBObjectStoreRequestChild); + MOZ_ASSERT(aHelper); + MOZ_ASSERT(aObjectStore); + MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && + aRequestType <= ParamsUnionType::T__Last); +} + +IndexedDBObjectStoreRequestChild::~IndexedDBObjectStoreRequestChild() +{ + MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestChild); +} + +bool +IndexedDBObjectStoreRequestChild::Recv__delete__(const ResponseValue& aResponse) +{ + switch (aResponse.type()) { + case ResponseValue::Tnsresult: + break; + case ResponseValue::TGetResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); + break; + case ResponseValue::TGetAllResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); + break; + case ResponseValue::TGetAllKeysResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); + break; + case ResponseValue::TAddResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams); + break; + case ResponseValue::TPutResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams); + break; + case ResponseValue::TDeleteResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TDeleteParams); + break; + case ResponseValue::TClearResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TClearParams); + break; + case ResponseValue::TCountResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); + break; + case ResponseValue::TOpenCursorResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams || + mRequestType == ParamsUnionType::TOpenKeyCursorParams); + break; + + default: + MOZ_CRASH("Received invalid response parameters!"); + } + + nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +/******************************************************************************* + * IndexedDBIndexRequestChild + ******************************************************************************/ + +IndexedDBIndexRequestChild::IndexedDBIndexRequestChild( + AsyncConnectionHelper* aHelper, + IDBIndex* aIndex, + RequestType aRequestType) +: IndexedDBRequestChildBase(aHelper), mIndex(aIndex), mRequestType(aRequestType) +{ + MOZ_COUNT_CTOR(IndexedDBIndexRequestChild); + MOZ_ASSERT(aHelper); + MOZ_ASSERT(aIndex); + MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && + aRequestType <= ParamsUnionType::T__Last); +} + +IndexedDBIndexRequestChild::~IndexedDBIndexRequestChild() +{ + MOZ_COUNT_DTOR(IndexedDBIndexRequestChild); +} + +bool +IndexedDBIndexRequestChild::Recv__delete__(const ResponseValue& aResponse) +{ + switch (aResponse.type()) { + case ResponseValue::Tnsresult: + break; + case ResponseValue::TGetResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); + break; + case ResponseValue::TGetKeyResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetKeyParams); + break; + case ResponseValue::TGetAllResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); + break; + case ResponseValue::TGetAllKeysResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); + break; + case ResponseValue::TCountResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); + break; + case ResponseValue::TOpenCursorResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams || + mRequestType == ParamsUnionType::TOpenKeyCursorParams); + break; + + default: + MOZ_CRASH("Received invalid response parameters!"); + } + + nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +/******************************************************************************* + * IndexedDBCursorRequestChild + ******************************************************************************/ + +IndexedDBCursorRequestChild::IndexedDBCursorRequestChild( + AsyncConnectionHelper* aHelper, + IDBCursor* aCursor, + RequestType aRequestType) +: IndexedDBRequestChildBase(aHelper), mCursor(aCursor), + mRequestType(aRequestType) +{ + MOZ_COUNT_CTOR(IndexedDBCursorRequestChild); + MOZ_ASSERT(aHelper); + MOZ_ASSERT(aCursor); + MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && + aRequestType <= ParamsUnionType::T__Last); +} + +IndexedDBCursorRequestChild::~IndexedDBCursorRequestChild() +{ + MOZ_COUNT_DTOR(IndexedDBCursorRequestChild); +} + +bool +IndexedDBCursorRequestChild::Recv__delete__(const ResponseValue& aResponse) +{ + switch (aResponse.type()) { + case ResponseValue::Tnsresult: + break; + case ResponseValue::TContinueResponse: + MOZ_ASSERT(mRequestType == ParamsUnionType::TContinueParams); + break; + + default: + MOZ_CRASH("Received invalid response parameters!"); + } + + nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +/******************************************************************************* + * IndexedDBDeleteDatabaseRequestChild + ******************************************************************************/ + +IndexedDBDeleteDatabaseRequestChild::IndexedDBDeleteDatabaseRequestChild( + IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + const nsACString& aDatabaseId) +: mFactory(aFactory), mOpenRequest(aOpenRequest), mDatabaseId(aDatabaseId) +{ + MOZ_COUNT_CTOR(IndexedDBDeleteDatabaseRequestChild); + MOZ_ASSERT(aFactory); + MOZ_ASSERT(aOpenRequest); + MOZ_ASSERT(!aDatabaseId.IsEmpty()); +} + +IndexedDBDeleteDatabaseRequestChild::~IndexedDBDeleteDatabaseRequestChild() +{ + MOZ_COUNT_DTOR(IndexedDBDeleteDatabaseRequestChild); +} + +bool +IndexedDBDeleteDatabaseRequestChild::Recv__delete__(const nsresult& aRv) +{ + nsRefPtr helper = + new IPCDeleteDatabaseHelper(mOpenRequest); + + if (NS_SUCCEEDED(aRv)) { + DatabaseInfo::Remove(mDatabaseId); + } + else { + helper->SetError(aRv); + } + + ImmediateRunEventTarget target; + if (NS_FAILED(helper->Dispatch(&target))) { + NS_WARNING("Dispatch of IPCSetVersionHelper failed!"); + return false; + } + + return true; +} + +bool +IndexedDBDeleteDatabaseRequestChild::RecvBlocked( + const uint64_t& aCurrentVersion) +{ + MOZ_ASSERT(mOpenRequest); + + nsCOMPtr runnable = + IDBVersionChangeEvent::CreateBlockedRunnable(mOpenRequest, + aCurrentVersion, 0); + + ImmediateRunEventTarget target; + if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) { + NS_WARNING("Dispatch of blocked event failed!"); + } + + return true; +} + +/******************************************************************************* + * Helpers + ******************************************************************************/ + +nsresult +IPCOpenDatabaseHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_NOTREACHED("Should never get here!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; +} + +AsyncConnectionHelper::ChildProcessSendResult +IPCOpenDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + MOZ_CRASH("Don't call me!"); +} + +nsresult +IPCOpenDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + MOZ_CRASH("Don't call me!"); +} + +nsresult +IPCOpenDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) +{ + return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), + aVal); +} + +nsresult +IPCSetVersionHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + NS_NOTREACHED("Should never get here!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; +} + +AsyncConnectionHelper::ChildProcessSendResult +IPCSetVersionHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + MOZ_CRASH("Don't call me!"); +} + +nsresult +IPCSetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + MOZ_CRASH("Don't call me!"); +} + +already_AddRefed +IPCSetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) +{ + return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner, + mOldVersion, + mRequestedVersion); +} + +nsresult +IPCSetVersionHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) +{ + mOpenRequest->SetTransaction(mTransaction); + + return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), + aVal); +} + +nsresult +IPCDeleteDatabaseHelper::UnpackResponseFromParentProcess( + const ResponseValue& aResponseValue) +{ + MOZ_CRASH("Don't call me!"); +} + +AsyncConnectionHelper::ChildProcessSendResult +IPCDeleteDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) +{ + MOZ_CRASH("Don't call me!"); +} + +nsresult +IPCDeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) +{ + aVal.setUndefined(); + return NS_OK; +} + +nsresult +IPCDeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +{ + MOZ_CRASH("Don't call me!"); +} diff --git a/dom/indexedDB/ipc/IndexedDBChild.h b/dom/indexedDB/ipc/IndexedDBChild.h new file mode 100644 index 000000000000..a003a0402ac1 --- /dev/null +++ b/dom/indexedDB/ipc/IndexedDBChild.h @@ -0,0 +1,457 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_ipc_indexeddbchild_h__ +#define mozilla_dom_indexeddb_ipc_indexeddbchild_h__ + +#include "mozilla/Attributes.h" +#include "mozilla/DebugOnly.h" + +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + +#include "mozilla/dom/indexedDB/PIndexedDBChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBCursorChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBDatabaseChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBDeleteDatabaseRequestChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBIndexChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBObjectStoreChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBRequestChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBTransactionChild.h" + +namespace mozilla { +namespace dom { +class ContentChild; +class TabChild; +} // dom +} // mozilla + +BEGIN_INDEXEDDB_NAMESPACE + +class AsyncConnectionHelper; +class IDBCursor; +class IDBFactory; +class IDBIndex; +class IDBOpenDBRequest; +class IDBRequest; +class IDBTransactionListener; + +/******************************************************************************* + * IndexedDBChild + ******************************************************************************/ + +class IndexedDBChild : public PIndexedDBChild +{ + IDBFactory* mFactory; + ContentChild* mManagerContent; + TabChild* mManagerTab; + + nsCString mASCIIOrigin; + +#ifdef DEBUG + bool mDisconnected; +#endif + +public: + IndexedDBChild(ContentChild* aContentChild, const nsCString& aASCIIOrigin); + IndexedDBChild(TabChild* aTabChild, const nsCString& aASCIIOrigin); + virtual ~IndexedDBChild(); + + const nsCString& + ASCIIOrigin() const + { + return mASCIIOrigin; + } + + ContentChild* + GetManagerContent() const + { + return mManagerContent; + } + + TabChild* + GetManagerTab() const + { + return mManagerTab; + } + + void + SetFactory(IDBFactory* aFactory); + + void + Disconnect(); + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual PIndexedDBDatabaseChild* + AllocPIndexedDBDatabaseChild(const nsString& aName, const uint64_t& aVersion, + const PersistenceType& aPersistenceType) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBDeleteDatabaseRequestChild* + AllocPIndexedDBDeleteDatabaseRequestChild( + const nsString& aName, + const PersistenceType& aPersistenceType) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBDeleteDatabaseRequestChild( + PIndexedDBDeleteDatabaseRequestChild* aActor) + MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBDatabaseChild + ******************************************************************************/ + +class IndexedDBDatabaseChild : public PIndexedDBDatabaseChild +{ + IDBDatabase* mDatabase; + nsString mName; + uint64_t mVersion; + + nsRefPtr mRequest; + nsRefPtr mOpenHelper; + + // Only used during version change transactions and blocked events. + nsRefPtr mStrongDatabase; + +public: + IndexedDBDatabaseChild(const nsString& aName, uint64_t aVersion); + virtual ~IndexedDBDatabaseChild(); + + void + SetRequest(IDBOpenDBRequest* aRequest); + + void + Disconnect(); + +protected: + bool + EnsureDatabase(IDBOpenDBRequest* aRequest, + const DatabaseInfoGuts& aDBInfo, + const InfallibleTArray& aOSInfo); + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvSuccess(const DatabaseInfoGuts& aDBInfo, + const InfallibleTArray& aOSInfo) + MOZ_OVERRIDE; + + virtual bool + RecvError(const nsresult& aRv) MOZ_OVERRIDE; + + virtual bool + RecvBlocked(const uint64_t& aOldVersion) MOZ_OVERRIDE; + + virtual bool + RecvVersionChange(const uint64_t& aOldVersion, const uint64_t& aNewVersion) + MOZ_OVERRIDE; + + virtual bool + RecvInvalidate() MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBTransactionConstructor(PIndexedDBTransactionChild* aActor, + const TransactionParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBTransactionChild* + AllocPIndexedDBTransactionChild(const TransactionParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBTransactionChild(PIndexedDBTransactionChild* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBTransactionChild + ******************************************************************************/ + +class IndexedDBTransactionChild : public PIndexedDBTransactionChild +{ + IDBTransaction* mTransaction; + + nsRefPtr mStrongTransaction; + nsRefPtr mTransactionListener; + +public: + IndexedDBTransactionChild(); + virtual ~IndexedDBTransactionChild(); + + void + SetTransaction(IDBTransaction* aTransaction); + + IDBTransaction* + GetTransaction() const + { + return mTransaction; + } + + void + Disconnect(); + +protected: + void + FireCompleteEvent(nsresult aRv); + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvComplete(const CompleteParams& aParams) MOZ_OVERRIDE; + + virtual PIndexedDBObjectStoreChild* + AllocPIndexedDBObjectStoreChild(const ObjectStoreConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBObjectStoreChild(PIndexedDBObjectStoreChild* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBObjectStoreChild + ******************************************************************************/ + +class IndexedDBObjectStoreChild : public PIndexedDBObjectStoreChild +{ + IDBObjectStore* mObjectStore; + +public: + explicit IndexedDBObjectStoreChild(IDBObjectStore* aObjectStore); + virtual ~IndexedDBObjectStoreChild(); + + void + Disconnect(); + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBCursorConstructor( + PIndexedDBCursorChild* aActor, + const ObjectStoreCursorConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBRequestChild* + AllocPIndexedDBRequestChild(const ObjectStoreRequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBIndexChild* + AllocPIndexedDBIndexChild(const IndexConstructorParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBIndexChild(PIndexedDBIndexChild* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBCursorChild* + AllocPIndexedDBCursorChild(const ObjectStoreCursorConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBCursorChild(PIndexedDBCursorChild* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBIndexChild + ******************************************************************************/ + +class IndexedDBIndexChild : public PIndexedDBIndexChild +{ + IDBIndex* mIndex; + +public: + explicit IndexedDBIndexChild(IDBIndex* aIndex); + virtual ~IndexedDBIndexChild(); + + void + Disconnect(); + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBCursorConstructor(PIndexedDBCursorChild* aActor, + const IndexCursorConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBRequestChild* + AllocPIndexedDBRequestChild(const IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBCursorChild* + AllocPIndexedDBCursorChild(const IndexCursorConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBCursorChild(PIndexedDBCursorChild* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBCursorChild + ******************************************************************************/ + +class IndexedDBCursorChild : public PIndexedDBCursorChild +{ + IDBCursor* mCursor; + + nsRefPtr mStrongCursor; + +public: + IndexedDBCursorChild(); + virtual ~IndexedDBCursorChild(); + + void + SetCursor(IDBCursor* aCursor); + + already_AddRefed + ForgetStrongCursor() + { + return mStrongCursor.forget(); + } + + void + Disconnect(); + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual PIndexedDBRequestChild* + AllocPIndexedDBRequestChild(const CursorRequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBRequestChildBase + ******************************************************************************/ + +class IndexedDBRequestChildBase : public PIndexedDBRequestChild +{ +protected: + nsRefPtr mHelper; + +public: + IDBRequest* + GetRequest() const; + + void + Disconnect(); + +protected: + explicit IndexedDBRequestChildBase(AsyncConnectionHelper* aHelper); + virtual ~IndexedDBRequestChildBase(); + + virtual bool + Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBObjectStoreRequestChild + ******************************************************************************/ + +class IndexedDBObjectStoreRequestChild : public IndexedDBRequestChildBase +{ + nsRefPtr mObjectStore; + + typedef ipc::ObjectStoreRequestParams ParamsUnionType; + typedef ParamsUnionType::Type RequestType; + DebugOnly mRequestType; + +public: + IndexedDBObjectStoreRequestChild(AsyncConnectionHelper* aHelper, + IDBObjectStore* aObjectStore, + RequestType aRequestType); + virtual ~IndexedDBObjectStoreRequestChild(); + +protected: + virtual bool + Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBIndexRequestChild + ******************************************************************************/ + +class IndexedDBIndexRequestChild : public IndexedDBRequestChildBase +{ + nsRefPtr mIndex; + + typedef ipc::IndexRequestParams ParamsUnionType; + typedef ParamsUnionType::Type RequestType; + DebugOnly mRequestType; + +public: + IndexedDBIndexRequestChild(AsyncConnectionHelper* aHelper, IDBIndex* aIndex, + RequestType aRequestType); + virtual ~IndexedDBIndexRequestChild(); + +protected: + virtual bool + Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBCursorRequestChild + ******************************************************************************/ + +class IndexedDBCursorRequestChild : public IndexedDBRequestChildBase +{ + nsRefPtr mCursor; + + typedef ipc::CursorRequestParams ParamsUnionType; + typedef ParamsUnionType::Type RequestType; + DebugOnly mRequestType; + +public: + IndexedDBCursorRequestChild(AsyncConnectionHelper* aHelper, + IDBCursor* aCursor, + RequestType aRequestType); + virtual ~IndexedDBCursorRequestChild(); + +protected: + virtual bool + Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBDeleteDatabaseRequestChild + ******************************************************************************/ + +class IndexedDBDeleteDatabaseRequestChild : + public PIndexedDBDeleteDatabaseRequestChild +{ + nsRefPtr mFactory; + nsRefPtr mOpenRequest; + nsCString mDatabaseId; + +public: + IndexedDBDeleteDatabaseRequestChild(IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + const nsACString& aDatabaseId); + virtual ~IndexedDBDeleteDatabaseRequestChild(); + +protected: + virtual bool + Recv__delete__(const nsresult& aRv) MOZ_OVERRIDE; + + virtual bool + RecvBlocked(const uint64_t& aCurrentVersion) MOZ_OVERRIDE; +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_ipc_indexeddbchild_h__ diff --git a/dom/indexedDB/ipc/IndexedDBParams.ipdlh b/dom/indexedDB/ipc/IndexedDBParams.ipdlh new file mode 100644 index 000000000000..d39e2cad9857 --- /dev/null +++ b/dom/indexedDB/ipc/IndexedDBParams.ipdlh @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; +using mozilla::dom::indexedDB::IDBCursor::Direction from "mozilla/dom/indexedDB/IDBCursor.h"; +using struct mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo from "mozilla/dom/indexedDB/IndexedDatabase.h"; + +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { +namespace ipc { + +struct KeyRange +{ + Key lower; + Key upper; + bool lowerOpen; + bool upperOpen; + bool isOnly; +}; + +union OptionalKeyRange +{ + KeyRange; + void_t; +}; + +struct GetParams +{ + KeyRange keyRange; +}; + +struct GetAllParams +{ + OptionalKeyRange optionalKeyRange; + uint32_t limit; +}; + +struct GetAllKeysParams +{ + OptionalKeyRange optionalKeyRange; + uint32_t limit; +}; + +struct CountParams +{ + OptionalKeyRange optionalKeyRange; +}; + +struct OpenCursorParams +{ + OptionalKeyRange optionalKeyRange; + Direction direction; +}; + +struct OpenKeyCursorParams +{ + OptionalKeyRange optionalKeyRange; + Direction direction; +}; + +union OptionalStructuredCloneReadInfo +{ + SerializedStructuredCloneReadInfo; + void_t; +}; + +} // namespace ipc +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/IndexedDBParent.cpp b/dom/indexedDB/ipc/IndexedDBParent.cpp new file mode 100644 index 000000000000..0265d0327db4 --- /dev/null +++ b/dom/indexedDB/ipc/IndexedDBParent.cpp @@ -0,0 +1,2262 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "IndexedDBParent.h" + +#include "nsIDOMEvent.h" +#include "nsIDOMFile.h" +#include "nsIXPConnect.h" + +#include "mozilla/AppProcessChecker.h" +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/IDBDatabaseBinding.h" +#include "mozilla/dom/ipc/Blob.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/unused.h" + +#include "AsyncConnectionHelper.h" +#include "DatabaseInfo.h" +#include "IDBDatabase.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IDBIndex.h" +#include "IDBKeyRange.h" +#include "IDBObjectStore.h" +#include "IDBTransaction.h" + +#define CHROME_ORIGIN "chrome" +#define PERMISSION_PREFIX "indexedDB-chrome-" +#define PERMISSION_SUFFIX_READ "-read" +#define PERMISSION_SUFFIX_WRITE "-write" + +USING_INDEXEDDB_NAMESPACE + +using namespace mozilla; +using namespace mozilla::dom; + +/******************************************************************************* + * AutoSetCurrentTransaction + ******************************************************************************/ + +AutoSetCurrentTransaction::AutoSetCurrentTransaction( + IDBTransaction* aTransaction) +{ + MOZ_ASSERT(aTransaction); + AsyncConnectionHelper::SetCurrentTransaction(aTransaction); +} + +AutoSetCurrentTransaction::~AutoSetCurrentTransaction() +{ + AsyncConnectionHelper::SetCurrentTransaction(nullptr); +} + +/******************************************************************************* + * IndexedDBParent + ******************************************************************************/ + +IndexedDBParent::IndexedDBParent(ContentParent* aContentParent) +: mManagerContent(aContentParent), mManagerTab(nullptr), mDisconnected(false) +{ + MOZ_COUNT_CTOR(IndexedDBParent); + MOZ_ASSERT(aContentParent); +} + +IndexedDBParent::IndexedDBParent(TabParent* aTabParent) +: mManagerContent(nullptr), mManagerTab(aTabParent), mDisconnected(false) +{ + MOZ_COUNT_CTOR(IndexedDBParent); + MOZ_ASSERT(aTabParent); +} + +IndexedDBParent::~IndexedDBParent() +{ + MOZ_COUNT_DTOR(IndexedDBParent); +} + +void +IndexedDBParent::Disconnect() +{ + if (mDisconnected) { + return; + } + + mDisconnected = true; + + const InfallibleTArray& databases = + ManagedPIndexedDBDatabaseParent(); + for (uint32_t i = 0; i < databases.Length(); ++i) { + static_cast(databases[i])->Disconnect(); + } +} + +bool +IndexedDBParent::CheckReadPermission(const nsAString& aDatabaseName) +{ + NS_NAMED_LITERAL_CSTRING(permission, PERMISSION_SUFFIX_READ); + return CheckPermissionInternal(aDatabaseName, permission); +} + +bool +IndexedDBParent::CheckWritePermission(const nsAString& aDatabaseName) +{ + // Write permission assumes read permission is granted as well. + MOZ_ASSERT(CheckReadPermission(aDatabaseName)); + + NS_NAMED_LITERAL_CSTRING(permission, PERMISSION_SUFFIX_WRITE); + return CheckPermissionInternal(aDatabaseName, permission); +} + +mozilla::ipc::IProtocol* +IndexedDBParent::CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) +{ + MOZ_ASSERT(mManagerContent != nullptr); + MOZ_ASSERT(mManagerTab == nullptr); + MOZ_ASSERT(!mDisconnected); + MOZ_ASSERT(IndexedDatabaseManager::Get()); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + ContentParent* contentParent = aCtx->GetContentParent(); + nsAutoPtr actor(contentParent->AllocPIndexedDBParent()); + if (!actor || !contentParent->RecvPIndexedDBConstructor(actor)) { + return nullptr; + } + return actor.forget(); +} + +bool +IndexedDBParent::CheckPermissionInternal(const nsAString& aDatabaseName, + const nsACString& aPermission) +{ + MOZ_ASSERT(!mASCIIOrigin.IsEmpty()); + MOZ_ASSERT(mManagerContent || mManagerTab); + + if (mASCIIOrigin.EqualsLiteral(CHROME_ORIGIN)) { + nsAutoCString fullPermission = + NS_LITERAL_CSTRING(PERMISSION_PREFIX) + + NS_ConvertUTF16toUTF8(aDatabaseName) + + aPermission; + + if ((mManagerContent && + !AssertAppProcessPermission(mManagerContent, fullPermission.get())) || + (mManagerTab && + !AssertAppProcessPermission(mManagerTab, fullPermission.get()))) { + return false; + } + } + + return true; +} + +void +IndexedDBParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // Nothing really needs to be done here... +} + +bool +IndexedDBParent::RecvPIndexedDBDatabaseConstructor( + PIndexedDBDatabaseParent* aActor, + const nsString& aName, + const uint64_t& aVersion, + const PersistenceType& aPersistenceType) +{ + if (!CheckReadPermission(aName)) { + return false; + } + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mFactory) { + return true; + } + + nsRefPtr request; + nsresult rv = mFactory->OpenInternal(aName, aVersion, aPersistenceType, false, + getter_AddRefs(request)); + NS_ENSURE_SUCCESS(rv, false); + + IndexedDBDatabaseParent* actor = + static_cast(aActor); + + rv = actor->SetOpenRequest(request); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +bool +IndexedDBParent::RecvPIndexedDBDeleteDatabaseRequestConstructor( + PIndexedDBDeleteDatabaseRequestParent* aActor, + const nsString& aName, + const PersistenceType& aPersistenceType) +{ + if (!CheckWritePermission(aName)) { + return false; + } + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mFactory) { + return true; + } + + IndexedDBDeleteDatabaseRequestParent* actor = + static_cast(aActor); + + nsRefPtr request; + + nsresult rv = mFactory->OpenInternal(aName, 0, aPersistenceType, true, + getter_AddRefs(request)); + NS_ENSURE_SUCCESS(rv, false); + + rv = actor->SetOpenRequest(request); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +PIndexedDBDatabaseParent* +IndexedDBParent::AllocPIndexedDBDatabaseParent( + const nsString& aName, + const uint64_t& aVersion, + const PersistenceType& aPersistenceType) +{ + return new IndexedDBDatabaseParent(); +} + +bool +IndexedDBParent::DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBDeleteDatabaseRequestParent* +IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequestParent( + const nsString& aName, + const PersistenceType& aPersistenceType) +{ + return new IndexedDBDeleteDatabaseRequestParent(mFactory); +} + +bool +IndexedDBParent::DeallocPIndexedDBDeleteDatabaseRequestParent( + PIndexedDBDeleteDatabaseRequestParent* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBDatabaseParent + ******************************************************************************/ + +IndexedDBDatabaseParent::IndexedDBDatabaseParent() +: mEventListener(MOZ_THIS_IN_INITIALIZER_LIST()) +{ + MOZ_COUNT_CTOR(IndexedDBDatabaseParent); +} + +IndexedDBDatabaseParent::~IndexedDBDatabaseParent() +{ + MOZ_COUNT_DTOR(IndexedDBDatabaseParent); +} + +nsresult +IndexedDBDatabaseParent::SetOpenRequest(IDBOpenDBRequest* aRequest) +{ + MOZ_ASSERT(aRequest); + MOZ_ASSERT(!mOpenRequest); + + nsresult rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(SUCCESS_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(BLOCKED_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(UPGRADENEEDED_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + mOpenRequest = aRequest; + return NS_OK; +} + +nsresult +IndexedDBDatabaseParent::HandleEvent(nsIDOMEvent* aEvent) +{ + MOZ_ASSERT(aEvent); + + if (IsDisconnected()) { + // We're shutting down, ignore this event. + return NS_OK; + } + + nsString type; + nsresult rv = aEvent->GetType(type); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr target = aEvent->InternalDOMEvent()->GetTarget(); + + if (mDatabase && + SameCOMIdentity(target, NS_ISUPPORTS_CAST(EventTarget*, + mDatabase))) { + rv = HandleDatabaseEvent(aEvent, type); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + if (mOpenRequest && + SameCOMIdentity(target, NS_ISUPPORTS_CAST(EventTarget*, + mOpenRequest))) { + rv = HandleRequestEvent(aEvent, type); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + MOZ_CRASH("Unexpected message!"); +} + +void +IndexedDBDatabaseParent::Disconnect() +{ + if (mDatabase) { + mDatabase->DisconnectFromActorParent(); + } +} + +bool +IndexedDBDatabaseParent::CheckWritePermission(const nsAString& aDatabaseName) +{ + IndexedDBParent* manager = static_cast(Manager()); + MOZ_ASSERT(manager); + + return manager->CheckWritePermission(aDatabaseName); +} + +void +IndexedDBDatabaseParent::Invalidate() +{ + MOZ_ASSERT(mDatabase); + + if (!IsDisconnected()) { + mozilla::unused << SendInvalidate(); + } +} + +nsresult +IndexedDBDatabaseParent::HandleRequestEvent(nsIDOMEvent* aEvent, + const nsAString& aType) +{ + MOZ_ASSERT(mOpenRequest); + MOZ_ASSERT(!IsDisconnected()); + + nsresult rv; + + if (aType.EqualsLiteral(ERROR_EVT_STR)) { + nsRefPtr request; + mOpenRequest.swap(request); + + rv = request->GetErrorCode(); + MOZ_ASSERT(NS_FAILED(rv)); + + if (!SendError(rv)) { + return NS_ERROR_FAILURE; + } + + rv = aEvent->PreventDefault(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + if (aType.EqualsLiteral(BLOCKED_EVT_STR)) { + MOZ_ASSERT(!mDatabase); + + nsCOMPtr changeEvent = do_QueryInterface(aEvent); + NS_ENSURE_TRUE(changeEvent, NS_ERROR_FAILURE); + + uint64_t oldVersion = changeEvent->OldVersion(); + + if (!SendBlocked(oldVersion)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + + AutoSafeJSContext cx; + + ErrorResult error; + JS::Rooted result(cx); + mOpenRequest->GetResult(cx, &result, error); + ENSURE_SUCCESS(error, error.ErrorCode()); + + MOZ_ASSERT(!result.isPrimitive()); + + IDBDatabase *database; + rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database); + if (NS_FAILED(rv)) { + NS_WARNING("Didn't get the object we expected!"); + return rv; + } + + DatabaseInfo* dbInfo = database->Info(); + MOZ_ASSERT(dbInfo); + + nsAutoTArray objectStoreNames; + if (!dbInfo->GetObjectStoreNames(objectStoreNames)) { + MOZ_CRASH("This should never fail!"); + } + + InfallibleTArray objectStoreInfos; + if (!objectStoreNames.IsEmpty()) { + uint32_t length = objectStoreNames.Length(); + + objectStoreInfos.SetCapacity(length); + + for (uint32_t i = 0; i < length; i++) { + ObjectStoreInfo* osInfo = dbInfo->GetObjectStore(objectStoreNames[i]); + MOZ_ASSERT(osInfo); + + objectStoreInfos.AppendElement(*osInfo); + } + } + + if (aType.EqualsLiteral(SUCCESS_EVT_STR)) { + nsRefPtr request; + mOpenRequest.swap(request); + + EventTarget* target = static_cast(database); + +#ifdef DEBUG + { + nsresult rvDEBUG = + target->AddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), + mEventListener, false); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rvDEBUG), "Failed to add error listener!"); + } +#endif + + NS_NAMED_LITERAL_STRING(versionChange, VERSIONCHANGE_EVT_STR); + rv = target->AddEventListener(versionChange, mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + if (!SendSuccess(*dbInfo, objectStoreInfos)) { + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(!mDatabase || mDatabase == database); + + if (!mDatabase) { + database->SetActor(this); + mDatabase = database; + } + + return NS_OK; + } + + if (aType.EqualsLiteral(UPGRADENEEDED_EVT_STR)) { + MOZ_ASSERT(!mDatabase); + + IDBTransaction* transaction = + AsyncConnectionHelper::GetCurrentTransaction(); + MOZ_ASSERT(transaction); + + if (!CheckWritePermission(database->Name())) { + // If we get here then the child process is either dead or in the process + // of being killed. Abort the transaction now to prevent any changes to + // the database. + ErrorResult rv; + transaction->Abort(rv); + if (rv.Failed()) { + NS_WARNING("Failed to abort transaction!"); + } + return NS_ERROR_FAILURE; + } + + nsCOMPtr changeEvent = do_QueryInterface(aEvent); + NS_ENSURE_TRUE(changeEvent, NS_ERROR_FAILURE); + + uint64_t oldVersion = changeEvent->OldVersion(); + + nsAutoPtr actor( + new IndexedDBVersionChangeTransactionParent()); + + rv = actor->SetTransaction(transaction); + NS_ENSURE_SUCCESS(rv, rv); + + VersionChangeTransactionParams versionChangeParams; + versionChangeParams.dbInfo() = *dbInfo; + versionChangeParams.osInfo() = objectStoreInfos; + versionChangeParams.oldVersion() = oldVersion; + + if (!SendPIndexedDBTransactionConstructor(actor.forget(), + versionChangeParams)) { + return NS_ERROR_FAILURE; + } + + database->SetActor(this); + mDatabase = database; + + return NS_OK; + } + + MOZ_CRASH("Unexpected message type!"); +} + +nsresult +IndexedDBDatabaseParent::HandleDatabaseEvent(nsIDOMEvent* aEvent, + const nsAString& aType) +{ + MOZ_ASSERT(mDatabase); + MOZ_ASSERT(!aType.EqualsLiteral(ERROR_EVT_STR), + "Should never get error events in the parent process!"); + MOZ_ASSERT(!IsDisconnected()); + + if (aType.EqualsLiteral(VERSIONCHANGE_EVT_STR)) { + AutoSafeJSContext cx; + NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); + + nsCOMPtr changeEvent = do_QueryInterface(aEvent); + NS_ENSURE_TRUE(changeEvent, NS_ERROR_FAILURE); + + uint64_t oldVersion = changeEvent->OldVersion(); + + Nullable newVersionVal = changeEvent->GetNewVersion(); + + uint64_t newVersion; + if (newVersionVal.IsNull()) { + newVersion = 0; + } + else { + newVersion = newVersionVal.Value(); + } + + if (!SendVersionChange(oldVersion, newVersion)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + + MOZ_CRASH("Unexpected message type!"); +} + +void +IndexedDBDatabaseParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mDatabase) { + mDatabase->SetActor(static_cast(nullptr)); + mDatabase->InvalidateInternal(/* aIsDead */ true); + } +} + +bool +IndexedDBDatabaseParent::RecvClose(const bool& aUnlinked) +{ + MOZ_ASSERT(mDatabase); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + mDatabase->CloseInternal(aUnlinked); + return true; +} + +bool +IndexedDBDatabaseParent::RecvPIndexedDBTransactionConstructor( + PIndexedDBTransactionParent* aActor, + const TransactionParams& aParams) +{ + MOZ_ASSERT(aParams.type() == + TransactionParams::TNormalTransactionParams); + MOZ_ASSERT(!mOpenRequest); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mDatabase) { + return true; + } + + IndexedDBTransactionParent* actor = + static_cast(aActor); + + const NormalTransactionParams& params = aParams.get_NormalTransactionParams(); + + if (params.mode() != IDBTransaction::READ_ONLY && + !CheckWritePermission(mDatabase->Name())) { + return false; + } + + if (mDatabase->IsClosed()) { + // If the window was navigated then we won't be able to do anything here. + return true; + } + + Sequence storesToOpen; + storesToOpen.AppendElements(params.names()); + + nsRefPtr transaction = + IDBTransaction::Create(mDatabase, storesToOpen, params.mode(), false); + NS_ENSURE_TRUE(transaction, false); + + nsresult rv = actor->SetTransaction(transaction); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +PIndexedDBTransactionParent* +IndexedDBDatabaseParent::AllocPIndexedDBTransactionParent( + const TransactionParams& aParams) +{ + MOZ_ASSERT(aParams.type() == + TransactionParams::TNormalTransactionParams); + return new IndexedDBTransactionParent(); +} + +bool +IndexedDBDatabaseParent::DeallocPIndexedDBTransactionParent( + PIndexedDBTransactionParent* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBTransactionParent + ******************************************************************************/ + +IndexedDBTransactionParent::IndexedDBTransactionParent() +: mEventListener(MOZ_THIS_IN_INITIALIZER_LIST()), + mArtificialRequestCount(false) +{ + MOZ_COUNT_CTOR(IndexedDBTransactionParent); +} + +IndexedDBTransactionParent::~IndexedDBTransactionParent() +{ + MOZ_COUNT_DTOR(IndexedDBTransactionParent); +} + +nsresult +IndexedDBTransactionParent::SetTransaction(IDBTransaction* aTransaction) +{ + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(!mTransaction); + + EventTarget* target = static_cast(aTransaction); + + NS_NAMED_LITERAL_STRING(complete, COMPLETE_EVT_STR); + nsresult rv = target->AddEventListener(complete, mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = target->AddEventListener(NS_LITERAL_STRING(ABORT_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + aTransaction->OnNewRequest(); + mArtificialRequestCount = true; + + aTransaction->SetActor(this); + + mTransaction = aTransaction; + return NS_OK; +} + +nsresult +IndexedDBTransactionParent::HandleEvent(nsIDOMEvent* aEvent) +{ + MOZ_ASSERT(aEvent); + + if (IsDisconnected()) { + // We're shutting down, ignore this event. + return NS_OK; + } + + nsString type; + nsresult rv = aEvent->GetType(type); + NS_ENSURE_SUCCESS(rv, rv); + + CompleteParams params; + + if (type.EqualsLiteral(COMPLETE_EVT_STR)) { + params = CompleteResult(); + } + else if (type.EqualsLiteral(ABORT_EVT_STR)) { +#ifdef DEBUG + { + nsCOMPtr target = aEvent->InternalDOMEvent()->GetTarget(); + MOZ_ASSERT(SameCOMIdentity(target, NS_ISUPPORTS_CAST(EventTarget*, + mTransaction))); + } +#endif + params = AbortResult(mTransaction->GetAbortCode()); + } + else { + NS_WARNING("Unknown message type!"); + return NS_ERROR_UNEXPECTED; + } + + if (!SendComplete(params)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +void +IndexedDBTransactionParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mTransaction) { + if (mArtificialRequestCount) { + // The transaction never completed and now the child side is dead. Abort + // here to be safe. + ErrorResult rv; + mTransaction->Abort(rv); + + mTransaction->OnRequestFinished(); +#ifdef DEBUG + mArtificialRequestCount = false; +#endif + } + mTransaction->SetActor(static_cast(nullptr)); + } +} + +bool +IndexedDBTransactionParent::RecvAbort(const nsresult& aAbortCode) +{ + MOZ_ASSERT(mTransaction); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + mTransaction->Abort(aAbortCode); + return true; +} + +bool +IndexedDBTransactionParent::RecvAllRequestsFinished() +{ + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mArtificialRequestCount); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + mTransaction->OnRequestFinished(); + mArtificialRequestCount = false; + + return true; +} + +bool +IndexedDBTransactionParent::RecvDeleteObjectStore(const nsString& aName) +{ + MOZ_CRASH("Should be overridden, don't call me!"); +} + +bool +IndexedDBTransactionParent::RecvPIndexedDBObjectStoreConstructor( + PIndexedDBObjectStoreParent* aActor, + const ObjectStoreConstructorParams& aParams) +{ + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mTransaction) { + return true; + } + + IndexedDBObjectStoreParent* actor = + static_cast(aActor); + + if (aParams.type() == + ObjectStoreConstructorParams::TGetObjectStoreParams) { + const GetObjectStoreParams& params = aParams.get_GetObjectStoreParams(); + const nsString& name = params.name(); + + nsRefPtr objectStore; + + { + AutoSetCurrentTransaction asct(mTransaction); + + ErrorResult rv; + objectStore = mTransaction->ObjectStore(name, rv); + ENSURE_SUCCESS(rv, false); + + actor->SetObjectStore(objectStore); + } + + objectStore->SetActor(actor); + return true; + } + + if (aParams.type() == + ObjectStoreConstructorParams::TCreateObjectStoreParams) { + MOZ_CRASH("Should be overridden, don't call me!"); + } + + MOZ_CRASH("Unknown param type!"); +} + +PIndexedDBObjectStoreParent* +IndexedDBTransactionParent::AllocPIndexedDBObjectStoreParent( + const ObjectStoreConstructorParams& aParams) +{ + return new IndexedDBObjectStoreParent(); +} + +bool +IndexedDBTransactionParent::DeallocPIndexedDBObjectStoreParent( + PIndexedDBObjectStoreParent* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBVersionChangeTransactionParent + ******************************************************************************/ + +IndexedDBVersionChangeTransactionParent:: + IndexedDBVersionChangeTransactionParent() +{ + MOZ_COUNT_CTOR(IndexedDBVersionChangeTransactionParent); +} + +IndexedDBVersionChangeTransactionParent:: + ~IndexedDBVersionChangeTransactionParent() +{ + MOZ_COUNT_DTOR(IndexedDBVersionChangeTransactionParent); +} + +bool +IndexedDBVersionChangeTransactionParent::RecvDeleteObjectStore( + const nsString& aName) +{ + MOZ_ASSERT(!mTransaction || + mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mTransaction) { + return true; + } + + if (mTransaction->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return true; + } + + IDBDatabase* db = mTransaction->Database(); + MOZ_ASSERT(db); + + ErrorResult rv; + + { + AutoSetCurrentTransaction asct(mTransaction); + db->DeleteObjectStore(aName, rv); + } + + ENSURE_SUCCESS(rv, false); + return true; +} + +bool +IndexedDBVersionChangeTransactionParent::RecvPIndexedDBObjectStoreConstructor( + PIndexedDBObjectStoreParent* aActor, + const ObjectStoreConstructorParams& aParams) +{ + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mTransaction) { + return true; + } + + if (mTransaction->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return true; + } + + IndexedDBObjectStoreParent* actor = + static_cast(aActor); + + if (aParams.type() == + ObjectStoreConstructorParams::TCreateObjectStoreParams) { + MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); + + const CreateObjectStoreParams& params = + aParams.get_CreateObjectStoreParams(); + + const ObjectStoreInfoGuts& info = params.info(); + + IDBDatabase* db = mTransaction->Database(); + MOZ_ASSERT(db); + + nsRefPtr objectStore; + + ErrorResult rv; + + { + AutoSetCurrentTransaction asct(mTransaction); + + objectStore = db->CreateObjectStoreInternal(mTransaction, info, rv); + } + + ENSURE_SUCCESS(rv, false); + + actor->SetObjectStore(objectStore); + objectStore->SetActor(actor); + return true; + } + + return + IndexedDBTransactionParent::RecvPIndexedDBObjectStoreConstructor(aActor, + aParams); +} + +PIndexedDBObjectStoreParent* +IndexedDBVersionChangeTransactionParent::AllocPIndexedDBObjectStoreParent( + const ObjectStoreConstructorParams& aParams) +{ + if (aParams.type() == + ObjectStoreConstructorParams::TCreateObjectStoreParams || + mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) { + return new IndexedDBVersionChangeObjectStoreParent(); + } + + return IndexedDBTransactionParent::AllocPIndexedDBObjectStoreParent(aParams); +} + +/******************************************************************************* + * IndexedDBCursorParent + ******************************************************************************/ + +IndexedDBCursorParent::IndexedDBCursorParent(IDBCursor* aCursor) +: mCursor(aCursor) +{ + MOZ_COUNT_CTOR(IndexedDBCursorParent); + MOZ_ASSERT(aCursor); + aCursor->SetActor(this); +} + +IndexedDBCursorParent::~IndexedDBCursorParent() +{ + MOZ_COUNT_DTOR(IndexedDBCursorParent); +} + +bool +IndexedDBCursorParent::IsDisconnected() const +{ + MOZ_ASSERT(mCursor); + return mCursor->Transaction()->GetActorParent()->IsDisconnected(); +} + +void +IndexedDBCursorParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_ASSERT(mCursor); + mCursor->SetActor(static_cast(nullptr)); +} + +bool +IndexedDBCursorParent::RecvPIndexedDBRequestConstructor( + PIndexedDBRequestParent* aActor, + const CursorRequestParams& aParams) +{ + MOZ_ASSERT(mCursor); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + IndexedDBCursorRequestParent* actor = + static_cast(aActor); + + if (mCursor->Transaction()->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + switch (aParams.type()) { + case CursorRequestParams::TContinueParams: + return actor->Continue(aParams.get_ContinueParams()); + + default: + MOZ_CRASH("Unknown type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +PIndexedDBRequestParent* +IndexedDBCursorParent::AllocPIndexedDBRequestParent( + const CursorRequestParams& aParams) +{ + MOZ_ASSERT(mCursor); + return new IndexedDBCursorRequestParent(mCursor, aParams.type()); +} + +bool +IndexedDBCursorParent::DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBObjectStoreParent + ******************************************************************************/ + +IndexedDBObjectStoreParent::IndexedDBObjectStoreParent() +{ + MOZ_COUNT_CTOR(IndexedDBObjectStoreParent); +} + +IndexedDBObjectStoreParent::~IndexedDBObjectStoreParent() +{ + MOZ_COUNT_DTOR(IndexedDBObjectStoreParent); +} + +void +IndexedDBObjectStoreParent::SetObjectStore(IDBObjectStore* aObjectStore) +{ + // Sadly can't assert aObjectStore here... + MOZ_ASSERT(!mObjectStore); + + mObjectStore = aObjectStore; +} + +void +IndexedDBObjectStoreParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mObjectStore) { + mObjectStore->SetActor(static_cast(nullptr)); + } +} + +bool +IndexedDBObjectStoreParent::RecvDeleteIndex(const nsString& aName) +{ + MOZ_CRASH("Should be overridden, don't call me!"); +} + +bool +IndexedDBObjectStoreParent::RecvPIndexedDBRequestConstructor( + PIndexedDBRequestParent* aActor, + const ObjectStoreRequestParams& aParams) +{ + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mObjectStore) { + return true; + } + + IndexedDBObjectStoreRequestParent* actor = + static_cast(aActor); + + if (mObjectStore->Transaction()->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + switch (aParams.type()) { + case ObjectStoreRequestParams::TGetParams: + return actor->Get(aParams.get_GetParams()); + + case ObjectStoreRequestParams::TGetAllParams: + return actor->GetAll(aParams.get_GetAllParams()); + + case ObjectStoreRequestParams::TGetAllKeysParams: + return actor->GetAllKeys(aParams.get_GetAllKeysParams()); + + case ObjectStoreRequestParams::TAddParams: + return actor->Add(aParams.get_AddParams()); + + case ObjectStoreRequestParams::TPutParams: + return actor->Put(aParams.get_PutParams()); + + case ObjectStoreRequestParams::TDeleteParams: + return actor->Delete(aParams.get_DeleteParams()); + + case ObjectStoreRequestParams::TClearParams: + return actor->Clear(aParams.get_ClearParams()); + + case ObjectStoreRequestParams::TCountParams: + return actor->Count(aParams.get_CountParams()); + + case ObjectStoreRequestParams::TOpenCursorParams: + return actor->OpenCursor(aParams.get_OpenCursorParams()); + + case ObjectStoreRequestParams::TOpenKeyCursorParams: + return actor->OpenKeyCursor(aParams.get_OpenKeyCursorParams()); + + default: + MOZ_CRASH("Unknown type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +bool +IndexedDBObjectStoreParent::RecvPIndexedDBIndexConstructor( + PIndexedDBIndexParent* aActor, + const IndexConstructorParams& aParams) +{ + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mObjectStore) { + return true; + } + + IndexedDBIndexParent* actor = static_cast(aActor); + + if (aParams.type() == IndexConstructorParams::TGetIndexParams) { + const GetIndexParams& params = aParams.get_GetIndexParams(); + const nsString& name = params.name(); + + nsRefPtr index; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + index = mObjectStore->Index(name, rv); + ENSURE_SUCCESS(rv, false); + + actor->SetIndex(index); + } + + index->SetActor(actor); + return true; + } + + if (aParams.type() == IndexConstructorParams::TCreateIndexParams) { + MOZ_CRASH("Should be overridden, don't call me!"); + } + + MOZ_CRASH("Unknown param type!"); +} + +PIndexedDBRequestParent* +IndexedDBObjectStoreParent::AllocPIndexedDBRequestParent( + const ObjectStoreRequestParams& aParams) +{ + return new IndexedDBObjectStoreRequestParent(mObjectStore, aParams.type()); +} + +bool +IndexedDBObjectStoreParent::DeallocPIndexedDBRequestParent( + PIndexedDBRequestParent* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBIndexParent* +IndexedDBObjectStoreParent::AllocPIndexedDBIndexParent( + const IndexConstructorParams& aParams) +{ + return new IndexedDBIndexParent(); +} + +bool +IndexedDBObjectStoreParent::DeallocPIndexedDBIndexParent( + PIndexedDBIndexParent* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBCursorParent* +IndexedDBObjectStoreParent::AllocPIndexedDBCursorParent( + const ObjectStoreCursorConstructorParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct a cursor!"); +} + +bool +IndexedDBObjectStoreParent::DeallocPIndexedDBCursorParent( + PIndexedDBCursorParent* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBVersionChangeObjectStoreParent + ******************************************************************************/ + +IndexedDBVersionChangeObjectStoreParent:: + IndexedDBVersionChangeObjectStoreParent() +{ + MOZ_COUNT_CTOR(IndexedDBVersionChangeObjectStoreParent); +} + +IndexedDBVersionChangeObjectStoreParent:: + ~IndexedDBVersionChangeObjectStoreParent() +{ + MOZ_COUNT_DTOR(IndexedDBVersionChangeObjectStoreParent); +} + +bool +IndexedDBVersionChangeObjectStoreParent::RecvDeleteIndex(const nsString& aName) +{ + MOZ_ASSERT(!mObjectStore || + mObjectStore->Transaction()->GetMode() == + IDBTransaction::VERSION_CHANGE); + + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mObjectStore) { + return true; + } + + if (mObjectStore->Transaction()->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return true; + } + + ErrorResult rv; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + mObjectStore->DeleteIndex(aName, rv); + } + + ENSURE_SUCCESS(rv, false); + return true; +} + +bool +IndexedDBVersionChangeObjectStoreParent::RecvPIndexedDBIndexConstructor( + PIndexedDBIndexParent* aActor, + const IndexConstructorParams& aParams) +{ + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mObjectStore) { + return true; + } + + if (mObjectStore->Transaction()->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return true; + } + + IndexedDBIndexParent* actor = static_cast(aActor); + + if (aParams.type() == IndexConstructorParams::TCreateIndexParams) { + MOZ_ASSERT(mObjectStore->Transaction()->GetMode() == + IDBTransaction::VERSION_CHANGE); + + const CreateIndexParams& params = aParams.get_CreateIndexParams(); + const IndexInfo& info = params.info(); + + nsRefPtr index; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + index = mObjectStore->CreateIndexInternal(info, rv); + ENSURE_SUCCESS(rv, false); + } + + actor->SetIndex(index); + index->SetActor(actor); + return true; + } + + return IndexedDBObjectStoreParent::RecvPIndexedDBIndexConstructor(aActor, + aParams); +} + +/******************************************************************************* + * IndexedDBIndexParent + ******************************************************************************/ + +IndexedDBIndexParent::IndexedDBIndexParent() +{ + MOZ_COUNT_CTOR(IndexedDBIndexParent); +} + +IndexedDBIndexParent::~IndexedDBIndexParent() +{ + MOZ_COUNT_DTOR(IndexedDBIndexParent); +} + +void +IndexedDBIndexParent::SetIndex(IDBIndex* aIndex) +{ + MOZ_ASSERT(aIndex); + MOZ_ASSERT(!mIndex); + + mIndex = aIndex; +} + +void +IndexedDBIndexParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mIndex) { + mIndex->SetActor(static_cast(nullptr)); + } +} + +bool +IndexedDBIndexParent::RecvPIndexedDBRequestConstructor( + PIndexedDBRequestParent* aActor, + const IndexRequestParams& aParams) +{ + if (IsDisconnected()) { + // We're shutting down, ignore this request. + return true; + } + + if (!mIndex) { + return true; + } + + IndexedDBIndexRequestParent* actor = + static_cast(aActor); + + if (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) { + // If we've invalidated this database in the parent then we should bail out + // now to avoid logic problems that could force-kill the child. + return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + + switch (aParams.type()) { + case IndexRequestParams::TGetParams: + return actor->Get(aParams.get_GetParams()); + + case IndexRequestParams::TGetKeyParams: + return actor->GetKey(aParams.get_GetKeyParams()); + + case IndexRequestParams::TGetAllParams: + return actor->GetAll(aParams.get_GetAllParams()); + + case IndexRequestParams::TGetAllKeysParams: + return actor->GetAllKeys(aParams.get_GetAllKeysParams()); + + case IndexRequestParams::TCountParams: + return actor->Count(aParams.get_CountParams()); + + case IndexRequestParams::TOpenCursorParams: + return actor->OpenCursor(aParams.get_OpenCursorParams()); + + case IndexRequestParams::TOpenKeyCursorParams: + return actor->OpenKeyCursor(aParams.get_OpenKeyCursorParams()); + + default: + MOZ_CRASH("Unknown type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +PIndexedDBRequestParent* +IndexedDBIndexParent::AllocPIndexedDBRequestParent(const IndexRequestParams& aParams) +{ + return new IndexedDBIndexRequestParent(mIndex, aParams.type()); +} + +bool +IndexedDBIndexParent::DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) +{ + delete aActor; + return true; +} + +PIndexedDBCursorParent* +IndexedDBIndexParent::AllocPIndexedDBCursorParent( + const IndexCursorConstructorParams& aParams) +{ + MOZ_CRASH("Caller is supposed to manually construct a cursor!"); +} + +bool +IndexedDBIndexParent::DeallocPIndexedDBCursorParent(PIndexedDBCursorParent* aActor) +{ + delete aActor; + return true; +} + +/******************************************************************************* + * IndexedDBRequestParentBase + ******************************************************************************/ + +IndexedDBRequestParentBase::IndexedDBRequestParentBase() +{ + MOZ_COUNT_CTOR(IndexedDBRequestParentBase); +} + +IndexedDBRequestParentBase::~IndexedDBRequestParentBase() +{ + MOZ_COUNT_DTOR(IndexedDBRequestParentBase); +} + +void +IndexedDBRequestParentBase::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mRequest) { + mRequest->SetActor(nullptr); + } +} + +/******************************************************************************* + * IndexedDBObjectStoreRequestParent + ******************************************************************************/ + +IndexedDBObjectStoreRequestParent::IndexedDBObjectStoreRequestParent( + IDBObjectStore* aObjectStore, + RequestType aRequestType) +: mObjectStore(aObjectStore), mRequestType(aRequestType) +{ + MOZ_COUNT_CTOR(IndexedDBObjectStoreRequestParent); + // Sadly can't assert aObjectStore here... + MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && + aRequestType <= ParamsUnionType::T__Last); +} + +IndexedDBObjectStoreRequestParent::~IndexedDBObjectStoreRequestParent() +{ + MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestParent); +} + +void +IndexedDBObjectStoreRequestParent::ConvertBlobActors( + const InfallibleTArray& aActors, + nsTArray >& aBlobs) +{ + MOZ_ASSERT(aBlobs.IsEmpty()); + MOZ_ASSERT(mObjectStore); + + if (!aActors.IsEmpty()) { + // Walk the chain to get to ContentParent. + MOZ_ASSERT(mObjectStore->Transaction()->Database()->GetContentParent()); + + uint32_t length = aActors.Length(); + aBlobs.SetCapacity(length); + + for (uint32_t index = 0; index < length; index++) { + BlobParent* actor = static_cast(aActors[index]); + aBlobs.AppendElement(actor->GetBlob()); + } + } +} + +bool +IndexedDBObjectStoreRequestParent::IsDisconnected() +{ + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(mObjectStore->GetActorParent()); + return mObjectStore->GetActorParent()->IsDisconnected(); +} + +bool +IndexedDBObjectStoreRequestParent::Get(const GetParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); + MOZ_ASSERT(mObjectStore); + + nsRefPtr request; + + nsRefPtr keyRange = + IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); + MOZ_ASSERT(keyRange); + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->GetInternal(keyRange, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + + return true; +} + +bool +IndexedDBObjectStoreRequestParent::GetAll(const GetAllParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); + MOZ_ASSERT(mObjectStore); + + nsRefPtr request; + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->GetAllInternal(keyRange, aParams.limit(), rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::GetAllKeys(const GetAllKeysParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); + MOZ_ASSERT(mObjectStore); + + nsRefPtr request; + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->GetAllKeysInternal(keyRange, aParams.limit(), rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::Add(const AddParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams); + MOZ_ASSERT(mObjectStore); + + ipc::AddPutParams params = aParams.commonParams(); + + nsTArray > blobs; + ConvertBlobActors(params.blobsParent(), blobs); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + nsresult rv = + mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(), + params.indexUpdateInfos(), blobs, false, + getter_AddRefs(request)); + NS_ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::Put(const PutParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams); + MOZ_ASSERT(mObjectStore); + + ipc::AddPutParams params = aParams.commonParams(); + + nsTArray > blobs; + ConvertBlobActors(params.blobsParent(), blobs); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + nsresult rv = + mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(), + params.indexUpdateInfos(), blobs, true, + getter_AddRefs(request)); + NS_ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::Delete(const DeleteParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TDeleteParams); + MOZ_ASSERT(mObjectStore); + + nsRefPtr request; + + nsRefPtr keyRange = + IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); + MOZ_ASSERT(keyRange); + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->DeleteInternal(keyRange, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::Clear(const ClearParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TClearParams); + MOZ_ASSERT(mObjectStore); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->Clear(rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::Count(const CountParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); + MOZ_ASSERT(mObjectStore); + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->CountInternal(keyRange, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::OpenCursor(const OpenCursorParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams); + MOZ_ASSERT(mObjectStore); + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + size_t direction = static_cast(aParams.direction()); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->OpenCursorInternal(keyRange, direction, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBObjectStoreRequestParent::OpenKeyCursor( + const OpenKeyCursorParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams); + MOZ_ASSERT(mObjectStore); + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + size_t direction = static_cast(aParams.direction()); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mObjectStore->Transaction()); + + ErrorResult rv; + request = mObjectStore->OpenKeyCursorInternal(keyRange, direction, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +/******************************************************************************* + * IndexedDBIndexRequestParent + ******************************************************************************/ + +IndexedDBIndexRequestParent::IndexedDBIndexRequestParent( + IDBIndex* aIndex, + RequestType aRequestType) +: mIndex(aIndex), mRequestType(aRequestType) +{ + MOZ_COUNT_CTOR(IndexedDBIndexRequestParent); + // Sadly can't assert aIndex here... + MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && + aRequestType <= ParamsUnionType::T__Last); +} + +IndexedDBIndexRequestParent::~IndexedDBIndexRequestParent() +{ + MOZ_COUNT_DTOR(IndexedDBIndexRequestParent); +} + +bool +IndexedDBIndexRequestParent::IsDisconnected() +{ + MOZ_ASSERT(mIndex); + MOZ_ASSERT(mIndex->GetActorParent()); + return mIndex->GetActorParent()->IsDisconnected(); +} + +bool +IndexedDBIndexRequestParent::Get(const GetParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); + MOZ_ASSERT(mIndex); + + nsRefPtr request; + + nsRefPtr keyRange = + IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); + MOZ_ASSERT(keyRange); + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + ErrorResult rv; + request = mIndex->GetInternal(keyRange, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBIndexRequestParent::GetKey(const GetKeyParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetKeyParams); + MOZ_ASSERT(mIndex); + + nsRefPtr request; + + nsRefPtr keyRange = + IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); + MOZ_ASSERT(keyRange); + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + ErrorResult rv; + request = mIndex->GetKeyInternal(keyRange, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBIndexRequestParent::GetAll(const GetAllParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); + MOZ_ASSERT(mIndex); + + nsRefPtr request; + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + ErrorResult rv; + request = mIndex->GetAllInternal(keyRange, aParams.limit(), rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBIndexRequestParent::GetAllKeys(const GetAllKeysParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); + MOZ_ASSERT(mIndex); + + nsRefPtr request; + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + ErrorResult rv; + request = mIndex->GetAllKeysInternal(keyRange, aParams.limit(), rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBIndexRequestParent::Count(const CountParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); + MOZ_ASSERT(mIndex); + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + ErrorResult rv; + request = mIndex->CountInternal(keyRange, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBIndexRequestParent::OpenCursor(const OpenCursorParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams); + MOZ_ASSERT(mIndex); + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + size_t direction = static_cast(aParams.direction()); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + nsresult rv = + mIndex->OpenCursorInternal(keyRange, direction, getter_AddRefs(request)); + NS_ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +bool +IndexedDBIndexRequestParent::OpenKeyCursor(const OpenKeyCursorParams& aParams) +{ + MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams); + MOZ_ASSERT(mIndex); + + const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); + + nsRefPtr keyRange; + + switch (keyRangeUnion.type()) { + case ipc::OptionalKeyRange::TKeyRange: + keyRange = + IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); + break; + + case ipc::OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Unknown param type!"); + } + + size_t direction = static_cast(aParams.direction()); + + nsRefPtr request; + + { + AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); + + ErrorResult rv; + request = mIndex->OpenKeyCursorInternal(keyRange, direction, rv); + ENSURE_SUCCESS(rv, false); + } + + request->SetActor(this); + mRequest.swap(request); + return true; +} + +/******************************************************************************* + * IndexedDBCursorRequestParent + ******************************************************************************/ + +IndexedDBCursorRequestParent::IndexedDBCursorRequestParent( + IDBCursor* aCursor, + RequestType aRequestType) +: mCursor(aCursor), mRequestType(aRequestType) +{ + MOZ_COUNT_CTOR(IndexedDBCursorRequestParent); + MOZ_ASSERT(aCursor); + MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && + aRequestType <= ParamsUnionType::T__Last); +} + +IndexedDBCursorRequestParent::~IndexedDBCursorRequestParent() +{ + MOZ_COUNT_DTOR(IndexedDBCursorRequestParent); +} + +bool +IndexedDBCursorRequestParent::IsDisconnected() +{ + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->GetActorParent()); + return mCursor->GetActorParent()->IsDisconnected(); +} + +bool +IndexedDBCursorRequestParent::Continue(const ContinueParams& aParams) +{ + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mRequestType == ParamsUnionType::TContinueParams); + + { + AutoSetCurrentTransaction asct(mCursor->Transaction()); + + ErrorResult rv; + mCursor->ContinueInternal(aParams.key(), aParams.count(), rv); + ENSURE_SUCCESS(rv, false); + } + + mRequest = mCursor->Request(); + MOZ_ASSERT(mRequest); + + mRequest->SetActor(this); + return true; +} + +/******************************************************************************* + * IndexedDBDeleteDatabaseRequestParent + ******************************************************************************/ + +IndexedDBDeleteDatabaseRequestParent::IndexedDBDeleteDatabaseRequestParent( + IDBFactory* aFactory) +: mEventListener(MOZ_THIS_IN_INITIALIZER_LIST()), mFactory(aFactory) +{ + MOZ_COUNT_CTOR(IndexedDBDeleteDatabaseRequestParent); + MOZ_ASSERT(aFactory); +} + +IndexedDBDeleteDatabaseRequestParent::~IndexedDBDeleteDatabaseRequestParent() +{ + MOZ_COUNT_DTOR(IndexedDBDeleteDatabaseRequestParent); +} + +nsresult +IndexedDBDeleteDatabaseRequestParent::HandleEvent(nsIDOMEvent* aEvent) +{ + MOZ_ASSERT(aEvent); + + if (IsDisconnected()) { + // We're shutting down, ignore this event. + return NS_OK; + } + + nsString type; + nsresult rv = aEvent->GetType(type); + NS_ENSURE_SUCCESS(rv, rv); + + if (type.EqualsASCII(BLOCKED_EVT_STR)) { + nsCOMPtr event = do_QueryInterface(aEvent); + MOZ_ASSERT(event); + + uint64_t currentVersion = event->OldVersion(); + + if (!SendBlocked(currentVersion)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + +#ifdef DEBUG + if (type.EqualsASCII(SUCCESS_EVT_STR)) { + MOZ_ASSERT(NS_SUCCEEDED(mOpenRequest->GetErrorCode())); + } + else { + MOZ_ASSERT(type.EqualsASCII(ERROR_EVT_STR)); + MOZ_ASSERT(NS_FAILED(mOpenRequest->GetErrorCode())); + } +#endif + + if (!Send__delete__(this, mOpenRequest->GetErrorCode())) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +void +IndexedDBDeleteDatabaseRequestParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // Implement me! Bug 1005149 +} + +nsresult +IndexedDBDeleteDatabaseRequestParent::SetOpenRequest( + IDBOpenDBRequest* aOpenRequest) +{ + MOZ_ASSERT(aOpenRequest); + MOZ_ASSERT(!mOpenRequest); + + EventTarget* target = static_cast(aOpenRequest); + + nsresult rv = target->AddEventListener(NS_LITERAL_STRING(SUCCESS_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = target->AddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = target->AddEventListener(NS_LITERAL_STRING(BLOCKED_EVT_STR), + mEventListener, false); + NS_ENSURE_SUCCESS(rv, rv); + + mOpenRequest = aOpenRequest; + return NS_OK; +} + +/******************************************************************************* + * WeakEventListener + ******************************************************************************/ + + NS_IMPL_ISUPPORTS(WeakEventListenerBase, nsIDOMEventListener) + + NS_IMETHODIMP + WeakEventListenerBase::HandleEvent(nsIDOMEvent* aEvent) +{ + MOZ_CRASH("This must be overridden!"); +} diff --git a/dom/indexedDB/ipc/IndexedDBParent.h b/dom/indexedDB/ipc/IndexedDBParent.h new file mode 100644 index 000000000000..355e54d11007 --- /dev/null +++ b/dom/indexedDB/ipc/IndexedDBParent.h @@ -0,0 +1,880 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_ipc_indexeddbparent_h__ +#define mozilla_dom_indexeddb_ipc_indexeddbparent_h__ + +#include "mozilla/Attributes.h" +#include "mozilla/DebugOnly.h" + +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + +#include "mozilla/dom/indexedDB/PIndexedDBParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBCursorParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBDatabaseParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBDeleteDatabaseRequestParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBIndexParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBObjectStoreParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBRequestParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBTransactionParent.h" + +#include "nsIDOMEventListener.h" + +namespace mozilla { +namespace dom { +class ContentParent; +class PBlobParent; +class TabParent; +} +} + +class nsIDOMBlob; +class nsIDOMEvent; + +BEGIN_INDEXEDDB_NAMESPACE + +class IDBCursor; +class IDBDatabase; +class IDBFactory; +class IDBIndex; +class IDBObjectStore; +class IDBOpenDBRequest; +class IDBTransaction; + +class IndexedDBCursorParent; +class IndexedDBDatabaseParent; +class IndexedDBDeleteDatabaseRequestParent; +class IndexedDBIndexParent; +class IndexedDBObjectStoreParent; +class IndexedDBTransactionParent; +class IndexedDBVersionChangeTransactionParent; +class IndexedDBVersionChangeObjectStoreParent; + +/******************************************************************************* + * AutoSetCurrentTransaction + ******************************************************************************/ + +class AutoSetCurrentTransaction +{ +public: + explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction); + ~AutoSetCurrentTransaction(); +}; + +/******************************************************************************* + * WeakEventListener + ******************************************************************************/ + +class WeakEventListenerBase : public nsIDOMEventListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTLISTENER + +protected: + WeakEventListenerBase() + { } + + virtual ~WeakEventListenerBase() + { } +}; + +template +class WeakEventListener : public WeakEventListenerBase +{ + T* mActor; + +public: + explicit WeakEventListener(T* aActor) + : mActor(aActor) + { } + + void + NoteDyingActor() + { + mActor = nullptr; + } + + NS_IMETHOD + HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE + { + return mActor ? mActor->HandleEvent(aEvent) : NS_OK; + } + +protected: + virtual ~WeakEventListener() + { } +}; + +template +class AutoWeakEventListener +{ + nsRefPtr > mEventListener; + +public: + explicit AutoWeakEventListener(T* aActor) + { + mEventListener = new WeakEventListener(aActor); + } + + ~AutoWeakEventListener() + { + mEventListener->NoteDyingActor(); + } + + template + operator U*() + { + return mEventListener; + } + + T* + operator ->() + { + return mEventListener; + } +}; + +/******************************************************************************* + * IndexedDBParent + ******************************************************************************/ + +class IndexedDBParent : private PIndexedDBParent +{ + friend class mozilla::dom::ContentParent; + friend class mozilla::dom::TabParent; + friend class IndexedDBDatabaseParent; + friend class IndexedDBDeleteDatabaseRequestParent; + + nsRefPtr mFactory; + nsCString mASCIIOrigin; + + ContentParent* mManagerContent; + TabParent* mManagerTab; + + bool mDisconnected; + +public: + explicit IndexedDBParent(ContentParent* aContentParent); + explicit IndexedDBParent(TabParent* aTabParent); + + virtual ~IndexedDBParent(); + + const nsCString& + GetASCIIOrigin() const + { + return mASCIIOrigin; + } + + void + Disconnect(); + + bool + IsDisconnected() const + { + return mDisconnected; + } + + ContentParent* + GetManagerContent() const + { + return mManagerContent; + } + + TabParent* + GetManagerTab() const + { + return mManagerTab; + } + + bool + CheckReadPermission(const nsAString& aDatabaseName); + + bool + CheckWritePermission(const nsAString& aDatabaseName); + + mozilla::ipc::IProtocol* + CloneProtocol(Channel* aChannel, + mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; + +protected: + bool + CheckPermissionInternal(const nsAString& aDatabaseName, + const nsACString& aPermission); + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBDatabaseConstructor(PIndexedDBDatabaseParent* aActor, + const nsString& aName, + const uint64_t& aVersion, + const PersistenceType& aPersistenceType) + MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBDeleteDatabaseRequestConstructor( + PIndexedDBDeleteDatabaseRequestParent* aActor, + const nsString& aName, + const PersistenceType& aPersistenceType) + MOZ_OVERRIDE; + + virtual PIndexedDBDatabaseParent* + AllocPIndexedDBDatabaseParent(const nsString& aName, const uint64_t& aVersion, + const PersistenceType& aPersistenceType) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBDeleteDatabaseRequestParent* + AllocPIndexedDBDeleteDatabaseRequestParent( + const nsString& aName, + const PersistenceType& aPersistenceType) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBDeleteDatabaseRequestParent( + PIndexedDBDeleteDatabaseRequestParent* aActor) + MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBDatabaseParent + ******************************************************************************/ + +class IndexedDBDatabaseParent : private PIndexedDBDatabaseParent +{ + friend class IndexedDBParent; + friend class IndexedDBTransactionParent; + friend class IndexedDBVersionChangeTransactionParent; + + AutoWeakEventListener mEventListener; + + nsRefPtr mOpenRequest; + nsRefPtr mDatabase; + +public: + IndexedDBDatabaseParent(); + virtual ~IndexedDBDatabaseParent(); + + nsresult + SetOpenRequest(IDBOpenDBRequest* aRequest); + + nsresult + HandleEvent(nsIDOMEvent* aEvent); + + void + Disconnect(); + + bool + IsDisconnected() const + { + return static_cast(Manager())->IsDisconnected(); + } + + bool + CheckWritePermission(const nsAString& aDatabaseName); + + void + Invalidate(); + +protected: + nsresult + HandleRequestEvent(nsIDOMEvent* aEvent, const nsAString& aType); + + nsresult + HandleDatabaseEvent(nsIDOMEvent* aEvent, const nsAString& aType); + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvClose(const bool& aUnlinked) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBTransactionConstructor(PIndexedDBTransactionParent* aActor, + const TransactionParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBTransactionParent* + AllocPIndexedDBTransactionParent(const TransactionParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBTransactionParent(PIndexedDBTransactionParent* aActor) + MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBTransactionParent + ******************************************************************************/ + +class IndexedDBTransactionParent : protected PIndexedDBTransactionParent +{ + friend class IndexedDBCursorParent; + friend class IndexedDBDatabaseParent; + friend class IndexedDBObjectStoreParent; + +protected: + AutoWeakEventListener mEventListener; + + nsRefPtr mTransaction; + + bool mArtificialRequestCount; + +public: + IndexedDBTransactionParent(); + virtual ~IndexedDBTransactionParent(); + + bool + IsDisconnected() const + { + return static_cast(Manager())->IsDisconnected(); + } + + nsresult + SetTransaction(IDBTransaction* aTransaction); + + IDBTransaction* + GetTransaction() const + { + return mTransaction; + } + + nsresult + HandleEvent(nsIDOMEvent* aEvent); + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvAbort(const nsresult& aAbortCode) MOZ_OVERRIDE; + + virtual bool + RecvAllRequestsFinished() MOZ_OVERRIDE; + + virtual bool + RecvDeleteObjectStore(const nsString& aName) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBObjectStoreConstructor( + PIndexedDBObjectStoreParent* aActor, + const ObjectStoreConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBObjectStoreParent* + AllocPIndexedDBObjectStoreParent(const ObjectStoreConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBObjectStoreParent(PIndexedDBObjectStoreParent* aActor) + MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBVersionChangeTransactionParent + ******************************************************************************/ + +class IndexedDBVersionChangeTransactionParent : + public IndexedDBTransactionParent +{ + friend class IndexedDBVersionChangeObjectStoreParent; + +public: + IndexedDBVersionChangeTransactionParent(); + virtual ~IndexedDBVersionChangeTransactionParent(); + + bool + IsDisconnected() const + { + return static_cast(Manager())->IsDisconnected(); + } + +protected: + virtual bool + RecvDeleteObjectStore(const nsString& aName) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBObjectStoreConstructor( + PIndexedDBObjectStoreParent* aActor, + const ObjectStoreConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBObjectStoreParent* + AllocPIndexedDBObjectStoreParent(const ObjectStoreConstructorParams& aParams) + MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBCursorParent + ******************************************************************************/ + +class IndexedDBCursorParent : private PIndexedDBCursorParent +{ + friend class IndexedDBIndexParent; + friend class IndexedDBObjectStoreParent; + + nsRefPtr mCursor; + +public: + IDBCursor* + GetCursor() const + { + return mCursor; + } + + bool + IsDisconnected() const; + +protected: + explicit IndexedDBCursorParent(IDBCursor* aCursor); + virtual ~IndexedDBCursorParent(); + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor, + const CursorRequestParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBRequestParent* + AllocPIndexedDBRequestParent(const CursorRequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBObjectStoreParent + ******************************************************************************/ + +class IndexedDBObjectStoreParent : protected PIndexedDBObjectStoreParent +{ + friend class IndexedDBIndexParent; + friend class IndexedDBTransactionParent; + friend class IndexedDBVersionChangeTransactionParent; + + typedef mozilla::dom::indexedDB::ipc::OpenCursorResponse OpenCursorResponse; + +protected: + nsRefPtr mObjectStore; + +public: + IndexedDBObjectStoreParent(); + virtual ~IndexedDBObjectStoreParent(); + + void + SetObjectStore(IDBObjectStore* aObjectStore); + + IDBObjectStore* + GetObjectStore() const + { + return mObjectStore; + } + + bool + IsDisconnected() const + { + IndexedDBTransactionParent* manager = + static_cast(Manager()); + return manager->IsDisconnected(); + } + + // Ordinarily callers could just do this manually using + // PIndexedDBObjectStoreParent::SendPIndexedDBCursorConstructor but we're + // inheriting the abstract protocol class privately to prevent outside code + // from sending messages without checking the disconnected state. Therefore + // we need a helper method. + bool + OpenCursor(IDBCursor* aCursor, + const ObjectStoreCursorConstructorParams& aParams, + OpenCursorResponse& aResponse) NS_WARN_UNUSED_RESULT + { + if (IsDisconnected()) { + return true; + } + + IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(aCursor); + + if (!SendPIndexedDBCursorConstructor(cursorActor, aParams)) { + return false; + } + + aResponse = cursorActor; + return true; + } + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvDeleteIndex(const nsString& aName) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor, + const ObjectStoreRequestParams& aParams) + MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBIndexConstructor(PIndexedDBIndexParent* aActor, + const IndexConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBRequestParent* + AllocPIndexedDBRequestParent(const ObjectStoreRequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBIndexParent* + AllocPIndexedDBIndexParent(const IndexConstructorParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBIndexParent(PIndexedDBIndexParent* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBCursorParent* + AllocPIndexedDBCursorParent(const ObjectStoreCursorConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBCursorParent(PIndexedDBCursorParent* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBVersionChangeObjectStoreParent + ******************************************************************************/ + +class IndexedDBVersionChangeObjectStoreParent : + public IndexedDBObjectStoreParent +{ + friend class IndexedDBVersionChangeTransactionParent; + +public: + IndexedDBVersionChangeObjectStoreParent(); + virtual ~IndexedDBVersionChangeObjectStoreParent(); + +protected: + bool + IsDisconnected() const + { + IndexedDBVersionChangeTransactionParent* manager = + static_cast(Manager()); + return manager->IsDisconnected(); + } + + virtual bool + RecvDeleteIndex(const nsString& aName) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBIndexConstructor(PIndexedDBIndexParent* aActor, + const IndexConstructorParams& aParams) + MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBIndexParent + ******************************************************************************/ + +class IndexedDBIndexParent : private PIndexedDBIndexParent +{ + friend class IndexedDBObjectStoreParent; + friend class IndexedDBVersionChangeObjectStoreParent; + + typedef mozilla::dom::indexedDB::ipc::OpenCursorResponse OpenCursorResponse; + + nsRefPtr mIndex; + +public: + IndexedDBIndexParent(); + virtual ~IndexedDBIndexParent(); + + void + SetIndex(IDBIndex* aObjectStore); + + IDBIndex* + GetIndex() const + { + return mIndex; + } + + // Ordinarily callers could just do this manually using + // PIndexedDBIndexParent::SendPIndexedDBCursorConstructor but we're + // inheriting the abstract protocol class privately to prevent outside code + // from sending messages without checking the disconnected state. Therefore + // we need a helper method. + bool + OpenCursor(IDBCursor* aCursor, const IndexCursorConstructorParams& aParams, + OpenCursorResponse& aResponse) NS_WARN_UNUSED_RESULT + { + if (IsDisconnected()) { + return true; + } + + IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(aCursor); + + if (!SendPIndexedDBCursorConstructor(cursorActor, aParams)) { + return false; + } + + aResponse = cursorActor; + return true; + } + + bool + IsDisconnected() const + { + IndexedDBObjectStoreParent* manager = + static_cast(Manager()); + return manager->IsDisconnected(); + } + +protected: + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor, + const IndexRequestParams& aParams) + MOZ_OVERRIDE; + + virtual PIndexedDBRequestParent* + AllocPIndexedDBRequestParent(const IndexRequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE; + + virtual PIndexedDBCursorParent* + AllocPIndexedDBCursorParent(const IndexCursorConstructorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPIndexedDBCursorParent(PIndexedDBCursorParent* aActor) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBRequestParentBase + ******************************************************************************/ + +class IndexedDBRequestParentBase : public PIndexedDBRequestParent +{ +public: + bool + SendResponse(const ResponseValue& aResponse) NS_WARN_UNUSED_RESULT + { + if (IsDisconnected()) { + return true; + } + + return Send__delete__(this, aResponse); + } + +protected: + // Don't let anyone call this directly, instead go through SendResponse. + using PIndexedDBRequestParent::Send__delete__; + + typedef ipc::ResponseValue ResponseValue; + typedef PIndexedDBRequestParent::PBlobParent PBlobParent; + + nsRefPtr mRequest; + + IndexedDBRequestParentBase(); + virtual ~IndexedDBRequestParentBase(); + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + IsDisconnected() = 0; +}; + +/******************************************************************************* + * IndexedDBObjectStoreRequestParent + ******************************************************************************/ + +class IndexedDBObjectStoreRequestParent : public IndexedDBRequestParentBase +{ + friend class IndexedDBObjectStoreParent; + + nsRefPtr mObjectStore; + + typedef ipc::ObjectStoreRequestParams ParamsUnionType; + typedef ParamsUnionType::Type RequestType; + DebugOnly mRequestType; + + typedef ipc::AddParams AddParams; + typedef ipc::PutParams PutParams; + typedef ipc::ClearParams ClearParams; + typedef ipc::DeleteParams DeleteParams; + typedef ipc::GetParams GetParams; + typedef ipc::GetAllParams GetAllParams; + typedef ipc::GetAllKeysParams GetAllKeysParams; + typedef ipc::CountParams CountParams; + typedef ipc::OpenCursorParams OpenCursorParams; + typedef ipc::OpenKeyCursorParams OpenKeyCursorParams; + +public: + IndexedDBObjectStoreRequestParent(IDBObjectStore* aObjectStore, + RequestType aRequestType); + virtual ~IndexedDBObjectStoreRequestParent(); + + bool + Get(const GetParams& aParams); + + bool + GetAll(const GetAllParams& aParams); + + bool + GetAllKeys(const GetAllKeysParams& aParams); + + bool + Add(const AddParams& aParams); + + bool + Put(const PutParams& aParams); + + bool + Delete(const DeleteParams& aParams); + + bool + Clear(const ClearParams& aParams); + + bool + Count(const CountParams& aParams); + + bool + OpenCursor(const OpenCursorParams& aParams); + + bool + OpenKeyCursor(const OpenKeyCursorParams& aParams); + +protected: + void + ConvertBlobActors(const InfallibleTArray& aActors, + nsTArray >& aBlobs); + +private: + virtual bool + IsDisconnected() MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBIndexRequestParent + ******************************************************************************/ + +class IndexedDBIndexRequestParent : public IndexedDBRequestParentBase +{ + friend class IndexedDBIndexParent; + + nsRefPtr mIndex; + + typedef ipc::IndexRequestParams ParamsUnionType; + typedef ParamsUnionType::Type RequestType; + DebugOnly mRequestType; + + typedef ipc::GetKeyParams GetKeyParams; + typedef ipc::GetAllKeysParams GetAllKeysParams; + typedef ipc::OpenKeyCursorParams OpenKeyCursorParams; + typedef ipc::GetParams GetParams; + typedef ipc::GetAllParams GetAllParams; + typedef ipc::CountParams CountParams; + typedef ipc::OpenCursorParams OpenCursorParams; + +public: + IndexedDBIndexRequestParent(IDBIndex* aIndex, RequestType aRequestType); + virtual ~IndexedDBIndexRequestParent(); + + bool + Get(const GetParams& aParams); + + bool + GetKey(const GetKeyParams& aParams); + + bool + GetAll(const GetAllParams& aParams); + + bool + GetAllKeys(const GetAllKeysParams& aParams); + + bool + Count(const CountParams& aParams); + + bool + OpenCursor(const OpenCursorParams& aParams); + + bool + OpenKeyCursor(const OpenKeyCursorParams& aParams); + +private: + virtual bool + IsDisconnected() MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBCursorRequestParent + ******************************************************************************/ + +class IndexedDBCursorRequestParent : public IndexedDBRequestParentBase +{ + friend class IndexedDBCursorParent; + + nsRefPtr mCursor; + + typedef ipc::CursorRequestParams ParamsUnionType; + typedef ParamsUnionType::Type RequestType; + DebugOnly mRequestType; + + typedef ipc::ContinueParams ContinueParams; + +public: + IndexedDBCursorRequestParent(IDBCursor* aCursor, RequestType aRequestType); + virtual ~IndexedDBCursorRequestParent(); + + bool + Continue(const ContinueParams& aParams); + +private: + virtual bool + IsDisconnected() MOZ_OVERRIDE; +}; + +/******************************************************************************* + * IndexedDBDeleteDatabaseRequestParent + ******************************************************************************/ + +class IndexedDBDeleteDatabaseRequestParent : + private PIndexedDBDeleteDatabaseRequestParent +{ + friend class IndexedDBParent; + + AutoWeakEventListener mEventListener; + + nsRefPtr mFactory; + nsRefPtr mOpenRequest; + +public: + nsresult + HandleEvent(nsIDOMEvent* aEvent); + +protected: + explicit IndexedDBDeleteDatabaseRequestParent(IDBFactory* aFactory); + virtual ~IndexedDBDeleteDatabaseRequestParent(); + + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + nsresult + SetOpenRequest(IDBOpenDBRequest* aOpenRequest); + + bool + IsDisconnected() const + { + return static_cast(Manager())->IsDisconnected(); + } +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_ipc_indexeddbparent_h__ diff --git a/dom/indexedDB/ipc/Makefile.in b/dom/indexedDB/ipc/Makefile.in new file mode 100644 index 000000000000..a5a459b7f249 --- /dev/null +++ b/dom/indexedDB/ipc/Makefile.in @@ -0,0 +1,14 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +include $(topsrcdir)/config/rules.mk + +xpcshell_tests = unit + +# Copy all the normal xpcshell tests from the regular unit directory. +copy-xpcshell-tests: + $(call install_cmd,$(wildcard $(topsrcdir)/dom/indexedDB/test/unit/test_*.js) \ + $(testxpcobjdir)/$(relativesrcdir)/$(xpcshell_tests)) + +libs-xpcshell-tests: copy-xpcshell-tests diff --git a/dom/indexedDB/ipc/PIndexedDB.ipdl b/dom/indexedDB/ipc/PIndexedDB.ipdl new file mode 100644 index 000000000000..4677685709ad --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDB.ipdl @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBrowser; +include protocol PContent; +include protocol PIndexedDBDatabase; +include protocol PIndexedDBDeleteDatabaseRequest; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using mozilla::dom::quota::PersistenceType from "mozilla/dom/quota/PersistenceType.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +protocol PIndexedDB +{ + manager PBrowser or PContent; + + manages PIndexedDBDatabase; + manages PIndexedDBDeleteDatabaseRequest; + +parent: + __delete__(); + + PIndexedDBDatabase(nsString name, uint64_t version, + PersistenceType persistenceType); + + PIndexedDBDeleteDatabaseRequest(nsString name, + PersistenceType persistenceType); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBCursor.ipdl b/dom/indexedDB/ipc/PIndexedDBCursor.ipdl new file mode 100644 index 000000000000..1daa93417355 --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDBCursor.ipdl @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PIndexedDBIndex; +include protocol PIndexedDBObjectStore; +include protocol PIndexedDBRequest; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; +using mozilla::dom::indexedDB::IDBCursor::Direction from "mozilla/dom/indexedDB/IDBCursor.h"; + +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +namespace ipc { + +struct ContinueParams +{ + Key key; + uint32_t count; +}; + +union CursorRequestParams +{ + ContinueParams; +}; + +} // namespace ipc + +protocol PIndexedDBCursor +{ + manager PIndexedDBObjectStore or PIndexedDBIndex; + + manages PIndexedDBRequest; + +parent: + __delete__(); + + PIndexedDBRequest(CursorRequestParams params); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBDatabase.ipdl b/dom/indexedDB/ipc/PIndexedDBDatabase.ipdl new file mode 100644 index 000000000000..f765687337cb --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDBDatabase.ipdl @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PIndexedDB; +include protocol PIndexedDBTransaction; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using struct mozilla::dom::indexedDB::DatabaseInfoGuts from "mozilla/dom/indexedDB/DatabaseInfo.h"; +using struct mozilla::dom::indexedDB::ObjectStoreInfoGuts from "mozilla/dom/indexedDB/DatabaseInfo.h"; +using mozilla::dom::indexedDB::IDBTransaction::Mode from "mozilla/dom/indexedDB/IDBTransaction.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +namespace ipc { + +struct NormalTransactionParams +{ + nsString[] names; + Mode mode; +}; + +struct VersionChangeTransactionParams +{ + DatabaseInfoGuts dbInfo; + ObjectStoreInfoGuts[] osInfo; + uint64_t oldVersion; +}; + +union TransactionParams +{ + NormalTransactionParams; + VersionChangeTransactionParams; +}; + +} // namespace ipc + +protocol PIndexedDBDatabase +{ + manager PIndexedDB; + + manages PIndexedDBTransaction; + +parent: + __delete__(); + + Close(bool unlinked); + +child: + Success(DatabaseInfoGuts dbInfo, ObjectStoreInfoGuts[] osInfo); + + Error(nsresult rv); + + Blocked(uint64_t oldVersion); + + VersionChange(uint64_t oldVersion, uint64_t newVersion); + + Invalidate(); + +both: + PIndexedDBTransaction(TransactionParams params); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl b/dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl similarity index 67% rename from dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl rename to dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl index db66e773394e..4e1f70482580 100644 --- a/dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl +++ b/dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl @@ -2,18 +2,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -include protocol PBackgroundIDBDatabase; +include protocol PIndexedDB; namespace mozilla { namespace dom { namespace indexedDB { -protocol PBackgroundIDBDatabaseFile +protocol PIndexedDBDeleteDatabaseRequest { - manager PBackgroundIDBDatabase; + manager PIndexedDB; -parent: - __delete__(); +child: + __delete__(nsresult rv); + + Blocked(uint64_t currentVersion); }; } // namespace indexedDB diff --git a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl new file mode 100644 index 000000000000..3b402fa5440e --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBlob; +include protocol PIndexedDBCursor; +include protocol PIndexedDBObjectStore; +include protocol PIndexedDBRequest; + +include IndexedDBParams; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +namespace ipc { + +struct GetKeyParams +{ + KeyRange keyRange; +}; + +union IndexRequestParams +{ + GetParams; + GetKeyParams; + GetAllParams; + GetAllKeysParams; + CountParams; + OpenCursorParams; + OpenKeyCursorParams; +}; + +struct IndexCursorConstructorParams +{ + PIndexedDBRequest request; + Direction direction; + Key key; + Key objectKey; + OptionalStructuredCloneReadInfo optionalCloneInfo; + PBlob[] blobs; +}; + +} // namespace ipc + +protocol PIndexedDBIndex +{ + manager PIndexedDBObjectStore; + + manages PIndexedDBCursor; + manages PIndexedDBRequest; + +parent: + __delete__(); + + PIndexedDBRequest(IndexRequestParams params); + +child: + PIndexedDBCursor(IndexCursorConstructorParams params); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl new file mode 100644 index 000000000000..8026db4086cd --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl @@ -0,0 +1,113 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBlob; +include protocol PIndexedDBCursor; +include protocol PIndexedDBIndex; +include protocol PIndexedDBRequest; +include protocol PIndexedDBTransaction; + +include IndexedDBParams; + +using struct mozilla::dom::indexedDB::IndexInfo from "mozilla/dom/indexedDB/DatabaseInfo.h"; +using struct mozilla::dom::indexedDB::IndexUpdateInfo from "mozilla/dom/indexedDB/DatabaseInfo.h"; +using struct mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo from "mozilla/dom/indexedDB/IndexedDatabase.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +namespace ipc { + +struct AddPutParams +{ + SerializedStructuredCloneWriteInfo cloneInfo; + Key key; + IndexUpdateInfo[] indexUpdateInfos; + PBlob[] blobs; +}; + +struct AddParams +{ + AddPutParams commonParams; +}; + +struct PutParams +{ + AddPutParams commonParams; +}; + +struct DeleteParams +{ + KeyRange keyRange; +}; + +struct ClearParams +{ +}; + +union ObjectStoreRequestParams +{ + GetParams; + GetAllParams; + GetAllKeysParams; + AddParams; + PutParams; + DeleteParams; + ClearParams; + CountParams; + OpenCursorParams; + OpenKeyCursorParams; +}; + +struct CreateIndexParams +{ + IndexInfo info; +}; + +struct GetIndexParams +{ + nsString name; +}; + +union IndexConstructorParams +{ + CreateIndexParams; + GetIndexParams; +}; + +struct ObjectStoreCursorConstructorParams +{ + PIndexedDBRequest request; + Direction direction; + Key key; + OptionalStructuredCloneReadInfo optionalCloneInfo; + PBlob[] blobs; +}; + +} // namespace ipc + +protocol PIndexedDBObjectStore +{ + manager PIndexedDBTransaction; + + manages PIndexedDBCursor; + manages PIndexedDBIndex; + manages PIndexedDBRequest; + +parent: + __delete__(); + + PIndexedDBIndex(IndexConstructorParams params); + PIndexedDBRequest(ObjectStoreRequestParams params); + + DeleteIndex(nsString name); + +child: + PIndexedDBCursor(ObjectStoreCursorConstructorParams params); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBRequest.ipdl b/dom/indexedDB/ipc/PIndexedDBRequest.ipdl new file mode 100644 index 000000000000..4c83fed55110 --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDBRequest.ipdl @@ -0,0 +1,113 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBlob; +include protocol PIndexedDBCursor; +include protocol PIndexedDBIndex; +include protocol PIndexedDBObjectStore; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; +using struct mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo from "mozilla/dom/indexedDB/IndexedDatabase.h"; + +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +namespace ipc { + +struct GetResponse +{ + SerializedStructuredCloneReadInfo cloneInfo; + PBlob[] blobs; +}; + +struct GetKeyResponse +{ + Key key; +}; + +struct BlobArray +{ + PBlob[] blobs; +}; + +struct GetAllResponse +{ + SerializedStructuredCloneReadInfo[] cloneInfos; + BlobArray[] blobs; +}; + +struct GetAllKeysResponse +{ + Key[] keys; +}; + +struct AddResponse +{ + Key key; +}; + +struct PutResponse +{ + Key key; +}; + +struct DeleteResponse +{ }; + +struct ClearResponse +{ }; + +struct CountResponse +{ + uint64_t count; +}; + +union OpenCursorResponse +{ + PIndexedDBCursor; + void_t; +}; + +struct ContinueResponse +{ + Key key; + Key objectKey; + SerializedStructuredCloneReadInfo cloneInfo; + PBlob[] blobs; +}; + +union ResponseValue +{ + nsresult; + GetResponse; + GetKeyResponse; + GetAllResponse; + GetAllKeysResponse; + AddResponse; + PutResponse; + DeleteResponse; + ClearResponse; + CountResponse; + OpenCursorResponse; + ContinueResponse; +}; + +} // namespace ipc + +protocol PIndexedDBRequest +{ + manager PIndexedDBObjectStore or PIndexedDBIndex or PIndexedDBCursor; + +child: + __delete__(ResponseValue response); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBTransaction.ipdl b/dom/indexedDB/ipc/PIndexedDBTransaction.ipdl new file mode 100644 index 000000000000..ff6d88a7b74f --- /dev/null +++ b/dom/indexedDB/ipc/PIndexedDBTransaction.ipdl @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PIndexedDBDatabase; +include protocol PIndexedDBObjectStore; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using struct mozilla::dom::indexedDB::ObjectStoreInfoGuts from "mozilla/dom/indexedDB/DatabaseInfo.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +namespace ipc { + +struct CreateObjectStoreParams +{ + ObjectStoreInfoGuts info; +}; + +struct GetObjectStoreParams +{ + nsString name; +}; + +union ObjectStoreConstructorParams +{ + CreateObjectStoreParams; + GetObjectStoreParams; +}; + +struct CompleteResult +{ }; + +struct AbortResult +{ + nsresult errorCode; +}; + +union CompleteParams +{ + CompleteResult; + AbortResult; +}; + +} // namespace ipc + +protocol PIndexedDBTransaction +{ + manager PIndexedDBDatabase; + + manages PIndexedDBObjectStore; + +parent: + __delete__(); + + PIndexedDBObjectStore(ObjectStoreConstructorParams params); + + Abort(nsresult abortCode); + + AllRequestsFinished(); + + DeleteObjectStore(nsString name); + +child: + Complete(CompleteParams params); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/SerializationHelpers.h b/dom/indexedDB/ipc/SerializationHelpers.h new file mode 100644 index 000000000000..3d6d4eb24aca --- /dev/null +++ b/dom/indexedDB/ipc/SerializationHelpers.h @@ -0,0 +1,309 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_serializationhelpers_h__ +#define mozilla_dom_indexeddb_serializationhelpers_h__ + +#include "ipc/IPCMessageUtils.h" + +#include "mozilla/dom/indexedDB/DatabaseInfo.h" +#include "mozilla/dom/indexedDB/Key.h" +#include "mozilla/dom/indexedDB/KeyPath.h" +#include "mozilla/dom/indexedDB/IDBCursor.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" + +namespace IPC { + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::quota::PersistenceType, + mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT, + mozilla::dom::quota::PERSISTENCE_TYPE_INVALID> +{ }; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::Key paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mBuffer); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mBuffer); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.mBuffer, aLog); + } +}; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::indexedDB::KeyPath::KeyPathType, + mozilla::dom::indexedDB::KeyPath::NONEXISTENT, + mozilla::dom::indexedDB::KeyPath::ENDGUARD> +{ }; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::KeyPath paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mType); + WriteParam(aMsg, aParam.mStrings); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mType) && + ReadParam(aMsg, aIter, &aResult->mStrings); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.mStrings, aLog); + } +}; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::indexedDB::IDBCursor::Direction, + mozilla::dom::indexedDB::IDBCursor::NEXT, + mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID> +{ }; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::indexedDB::IDBTransaction::Mode, + mozilla::dom::indexedDB::IDBTransaction::READ_ONLY, + mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID> +{ }; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::IndexInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.name); + WriteParam(aMsg, aParam.id); + WriteParam(aMsg, aParam.keyPath); + WriteParam(aMsg, aParam.unique); + WriteParam(aMsg, aParam.multiEntry); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->name) && + ReadParam(aMsg, aIter, &aResult->id) && + ReadParam(aMsg, aIter, &aResult->keyPath) && + ReadParam(aMsg, aIter, &aResult->unique) && + ReadParam(aMsg, aIter, &aResult->multiEntry); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.name, aLog); + LogParam(aParam.id, aLog); + LogParam(aParam.keyPath, aLog); + LogParam(aParam.unique, aLog); + LogParam(aParam.multiEntry, aLog); + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::ObjectStoreInfoGuts paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.name); + WriteParam(aMsg, aParam.id); + WriteParam(aMsg, aParam.keyPath); + WriteParam(aMsg, aParam.autoIncrement); + WriteParam(aMsg, aParam.indexes); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->name) && + ReadParam(aMsg, aIter, &aResult->id) && + ReadParam(aMsg, aIter, &aResult->keyPath) && + ReadParam(aMsg, aIter, &aResult->autoIncrement) && + ReadParam(aMsg, aIter, &aResult->indexes); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.name, aLog); + LogParam(aParam.id, aLog); + LogParam(aParam.keyPath, aLog); + LogParam(aParam.autoIncrement, aLog); + LogParam(aParam.indexes, aLog); + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::DatabaseInfoGuts paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.name); + WriteParam(aMsg, aParam.group); + WriteParam(aMsg, aParam.origin); + WriteParam(aMsg, aParam.version); + WriteParam(aMsg, aParam.persistenceType); + WriteParam(aMsg, aParam.nextObjectStoreId); + WriteParam(aMsg, aParam.nextIndexId); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->name) && + ReadParam(aMsg, aIter, &aResult->group) && + ReadParam(aMsg, aIter, &aResult->origin) && + ReadParam(aMsg, aIter, &aResult->version) && + ReadParam(aMsg, aIter, &aResult->persistenceType) && + ReadParam(aMsg, aIter, &aResult->nextObjectStoreId) && + ReadParam(aMsg, aIter, &aResult->nextIndexId); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.name, aLog); + LogParam(aParam.group, aLog); + LogParam(aParam.origin, aLog); + LogParam(aParam.version, aLog); + LogParam(aParam.nextObjectStoreId, aLog); + LogParam(aParam.nextIndexId, aLog); + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::IndexUpdateInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.indexId); + WriteParam(aMsg, aParam.indexUnique); + WriteParam(aMsg, aParam.value); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->indexId) && + ReadParam(aMsg, aIter, &aResult->indexUnique) && + ReadParam(aMsg, aIter, &aResult->value); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.indexId, aLog); + LogParam(aParam.indexUnique, aLog); + LogParam(aParam.value, aLog); + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.dataLength); + if (aParam.dataLength) { + aMsg->WriteBytes(aParam.data, aParam.dataLength); + } + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &aResult->dataLength)) { + return false; + } + + if (aResult->dataLength) { + const char** buffer = + const_cast(reinterpret_cast(&aResult->data)); + if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength)) { + return false; + } + } else { + aResult->data = nullptr; + } + + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.dataLength, aLog); + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.dataLength); + if (aParam.dataLength) { + aMsg->WriteBytes(aParam.data, aParam.dataLength); + } + WriteParam(aMsg, aParam.offsetToKeyProp); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (!ReadParam(aMsg, aIter, &aResult->dataLength)) { + return false; + } + + if (aResult->dataLength) { + const char** buffer = + const_cast(reinterpret_cast(&aResult->data)); + if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength)) { + return false; + } + } else { + aResult->data = nullptr; + } + + if (!ReadParam(aMsg, aIter, &aResult->offsetToKeyProp)) { + return false; + } + + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.dataLength, aLog); + LogParam(aParam.offsetToKeyProp, aLog); + } +}; + +} // namespace IPC + +#endif // mozilla_dom_indexeddb_serializationhelpers_h__ diff --git a/dom/indexedDB/ipc/mochitest.ini b/dom/indexedDB/ipc/mochitest.ini new file mode 100644 index 000000000000..81b48267ea25 --- /dev/null +++ b/dom/indexedDB/ipc/mochitest.ini @@ -0,0 +1,4 @@ +[DEFAULT] + +[test_ipc.html] +skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #bug 783513 # b2g(nested ipc not working) b2g-debug(nested ipc not working) b2g-desktop(nested ipc not working) diff --git a/dom/indexedDB/ipc/moz.build b/dom/indexedDB/ipc/moz.build new file mode 100644 index 000000000000..fee6defb45ea --- /dev/null +++ b/dom/indexedDB/ipc/moz.build @@ -0,0 +1,41 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla.dom.indexedDB += [ + 'SerializationHelpers.h', +] + +# Need to enable these tests sometime soon. +#XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini'] + +UNIFIED_SOURCES += [ + 'IndexedDBChild.cpp', + 'IndexedDBParent.cpp', +] + +IPDL_SOURCES += [ + 'IndexedDBParams.ipdlh', + 'PIndexedDB.ipdl', + 'PIndexedDBCursor.ipdl', + 'PIndexedDBDatabase.ipdl', + 'PIndexedDBDeleteDatabaseRequest.ipdl', + 'PIndexedDBIndex.ipdl', + 'PIndexedDBObjectStore.ipdl', + 'PIndexedDBRequest.ipdl', + 'PIndexedDBTransaction.ipdl', +] + +FAIL_ON_WARNINGS = True + +MOCHITEST_MANIFESTS += ['mochitest.ini'] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' +LOCAL_INCLUDES += [ + '/dom/indexedDB', +] + diff --git a/dom/indexedDB/ipc/unit/head.js b/dom/indexedDB/ipc/unit/head.js new file mode 100644 index 000000000000..8293b09c48fa --- /dev/null +++ b/dom/indexedDB/ipc/unit/head.js @@ -0,0 +1,18 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const INDEXEDDB_UNIT_DIR = "../../test/unit/"; +const INDEXEDDB_HEAD_FILE = INDEXEDDB_UNIT_DIR + "head.js"; + +function run_test() { + // IndexedDB needs a profile. + do_get_profile(); + + let thisTest = _TEST_FILE.toString().replace(/\\/g, "/"); + thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1); + + _HEAD_FILES.push(do_get_file(INDEXEDDB_HEAD_FILE).path.replace(/\\/g, "/")); + run_test_in_child(INDEXEDDB_UNIT_DIR + thisTest); +} diff --git a/dom/indexedDB/ipc/unit/xpcshell.ini b/dom/indexedDB/ipc/unit/xpcshell.ini new file mode 100644 index 000000000000..6aeedab2890a --- /dev/null +++ b/dom/indexedDB/ipc/unit/xpcshell.ini @@ -0,0 +1,68 @@ +[DEFAULT] +head = head.js +tail = + +# When adding files here please also update test/unit/xpcshell.ini! + +[test_add_put.js] +[test_add_twice_failure.js] +[test_advance.js] +[test_autoIncrement.js] +[test_autoIncrement_indexes.js] +[test_clear.js] +[test_complex_keyPaths.js] +[test_count.js] +[test_create_index.js] +[test_create_index_with_integer_keys.js] +[test_create_objectStore.js] +[test_cursor_mutation.js] +[test_cursor_update_updates_indexes.js] +[test_cursors.js] +[test_deleteDatabase.js] +[test_deleteDatabase_interactions.js] +[test_event_source.js] +[test_getAll.js] +[test_global_data.js] +[test_index_empty_keyPath.js] +[test_index_getAll.js] +[test_index_getAllObjects.js] +[test_index_object_cursors.js] +[test_index_update_delete.js] +[test_indexes.js] +[test_indexes_bad_values.js] +[test_key_requirements.js] +[test_keys.js] +[test_multientry.js] +[test_names_sorted.js] +[test_object_identity.js] +[test_objectCursors.js] +[test_objectStore_getAllKeys.js] +[test_objectStore_inline_autoincrement_key_added_on_put.js] +[test_objectStore_openKeyCursor.js] +[test_objectStore_remove_values.js] +[test_odd_result_order.js] +[test_open_empty_db.js] +[test_open_objectStore.js] +[test_optionalArguments.js] +[test_overlapping_transactions.js] +[test_put_get_values.js] +[test_put_get_values_autoIncrement.js] +[test_readonly_transactions.js] +[test_remove_index.js] +[test_remove_objectStore.js] +[test_request_readyState.js] +[test_setVersion.js] +[test_setVersion_abort.js] +[test_setVersion_events.js] +[test_setVersion_exclusion.js] +[test_success_events_after_abort.js] +[test_traffic_jam.js] +[test_transaction_abort.js] +[test_transaction_error.js] +[test_transaction_lifetimes.js] +[test_transaction_lifetimes_nested.js] +[test_transaction_ordering.js] +[test_unique_index_update.js] +[test_writer_starvation.js] + +# When adding files here please also update test/unit/xpcshell.ini! diff --git a/dom/indexedDB/moz.build b/dom/indexedDB/moz.build index ce1e78ef96a9..5c4071dd8970 100644 --- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -4,21 +4,12 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +DIRS += ['ipc'] TEST_DIRS += ['test/extensions'] -MOCHITEST_MANIFESTS += ['test/mochitest.ini'] - -BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] - -MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] - -XPCSHELL_TESTS_MANIFESTS += [ - 'test/unit/xpcshell-child-process.ini', - 'test/unit/xpcshell-parent-process.ini' -] - EXPORTS.mozilla.dom.indexedDB += [ - 'ActorsParent.h', + 'Client.h', + 'DatabaseInfo.h', 'FileInfo.h', 'FileManager.h', 'FileSnapshot.h', @@ -39,61 +30,60 @@ EXPORTS.mozilla.dom.indexedDB += [ 'IndexedDatabaseManager.h', 'Key.h', 'KeyPath.h', - 'SerializationHelpers.h', ] UNIFIED_SOURCES += [ - 'ActorsChild.cpp', + 'AsyncConnectionHelper.cpp', + 'CheckPermissionsHelper.cpp', + 'Client.cpp', + 'DatabaseInfo.cpp', 'FileInfo.cpp', + 'FileManager.cpp', 'FileSnapshot.cpp', - 'IDBCursor.cpp', 'IDBDatabase.cpp', 'IDBEvents.cpp', 'IDBFactory.cpp', 'IDBFileHandle.cpp', 'IDBFileRequest.cpp', - 'IDBIndex.cpp', 'IDBKeyRange.cpp', 'IDBMutableFile.cpp', - 'IDBObjectStore.cpp', 'IDBRequest.cpp', 'IDBTransaction.cpp', 'IDBWrapperCache.cpp', 'IndexedDatabaseManager.cpp', 'Key.cpp', 'KeyPath.cpp', - 'PermissionRequestBase.cpp', - 'ReportInternalError.cpp', + 'OpenDatabaseHelper.cpp', 'TransactionThreadPool.cpp', ] +# These files cannot be built in unified mode because of name collisions SOURCES += [ - 'ActorsParent.cpp', # This file is huge. + 'IDBCursor.cpp', + 'IDBIndex.cpp', + 'IDBObjectStore.cpp', + 'ReportInternalError.cpp', ] -IPDL_SOURCES += [ - 'PBackgroundIDBCursor.ipdl', - 'PBackgroundIDBDatabase.ipdl', - 'PBackgroundIDBDatabaseFile.ipdl', - 'PBackgroundIDBFactory.ipdl', - 'PBackgroundIDBFactoryRequest.ipdl', - 'PBackgroundIDBRequest.ipdl', - 'PBackgroundIDBSharedTypes.ipdlh', - 'PBackgroundIDBTransaction.ipdl', - 'PBackgroundIDBVersionChangeTransaction.ipdl', - 'PIndexedDBPermissionRequest.ipdl', -] +FAIL_ON_WARNINGS = True include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' - LOCAL_INCLUDES += [ + '/caps', '/content/base/src', '/db/sqlite3/src', '/dom/base', '/dom/quota', '/dom/storage', - '/ipc/glue', '/xpcom/build', ] + +MOCHITEST_MANIFESTS += [ + 'test/mochitest.ini', + 'test/unit/mochitest.ini', +] +BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] +MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] +XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] diff --git a/dom/indexedDB/test/browser.ini b/dom/indexedDB/test/browser.ini index 55ede42c0e7a..62a6254d1d5a 100644 --- a/dom/indexedDB/test/browser.ini +++ b/dom/indexedDB/test/browser.ini @@ -1,28 +1,24 @@ -[DEFAULT] -run-if = buildapp == "browser" -skip-if = e10s -support-files = - head.js - browser_forgetThisSiteAdd.html - browser_forgetThisSiteGet.html - browserHelpers.js - browser_permissionsPrompt.html - browser_quotaPrompt.html - browser_quotaPromptDatabases.html - browser_quotaPromptDelete.html - bug839193.js - bug839193.xul - -[browser_forgetThisSite.js] -[browser_permissionsPromptAllow.js] -[browser_permissionsPromptDeny.js] -[browser_perwindow_privateBrowsing.js] -[browser_quotaPromptAllow.js] -skip-if = true # Quota handling disabled for now. -[browser_quotaPromptDeny.js] -skip-if = true # Quota handling disabled for now. -[browser_quotaPromptDatabases.js] -skip-if = true # Quota handling disabled for now. -[browser_quotaPromptDelete.js] -skip-if = true # Quota handling disabled for now. -[browser_bug839193.js] +[DEFAULT] +run-if = buildapp == "browser" +skip-if = e10s +support-files = + head.js + browser_forgetThisSiteAdd.html + browser_forgetThisSiteGet.html + browserHelpers.js + browser_permissionsPrompt.html + browser_quotaPrompt.html + browser_quotaPromptDatabases.html + browser_quotaPromptDelete.html + bug839193.js + bug839193.xul + +[browser_forgetThisSite.js] +[browser_permissionsPromptAllow.js] +[browser_permissionsPromptDeny.js] +[browser_perwindow_privateBrowsing.js] +[browser_quotaPromptAllow.js] +[browser_quotaPromptDeny.js] +[browser_quotaPromptDatabases.js] +[browser_quotaPromptDelete.js] +[browser_bug839193.js] diff --git a/dom/indexedDB/test/file.js b/dom/indexedDB/test/file.js index e8fc24a12891..b9b735c5d557 100644 --- a/dom/indexedDB/test/file.js +++ b/dom/indexedDB/test/file.js @@ -8,6 +8,13 @@ const DEFAULT_QUOTA = 50 * 1024 * 1024; var bufferCache = []; var utils = SpecialPowers.getDOMWindowUtils(window); +if (!SpecialPowers.isMainProcess()) { + window.runTest = function() { + todo(false, "Test disabled in child processes, for now"); + finishTest(); + } +} + function getBuffer(size) { let buffer = new ArrayBuffer(size); @@ -182,17 +189,22 @@ function grabFileUsageAndContinueHandler(usage, fileUsage) function getUsage(usageHandler) { - let principal = SpecialPowers.wrap(document).nodePrincipal; - let appId, inBrowser; - if (principal.appId != Components.interfaces.nsIPrincipal.UNKNOWN_APP_ID && - principal.appId != Components.interfaces.nsIPrincipal.NO_APP_ID) { - appId = principal.appId; - inBrowser = principal.isInBrowserElement; - } - SpecialPowers.getStorageUsageForURI(window.document.documentURI, - usageHandler, - appId, - inBrowser); + let comp = SpecialPowers.wrap(Components); + let quotaManager = comp.classes["@mozilla.org/dom/quota/manager;1"] + .getService(comp.interfaces.nsIQuotaManager); + + // We need to pass a JS callback to getUsageForURI. However, that callback + // takes an XPCOM URI object, which will cause us to throw when we wrap it + // for the content compartment. So we need to define the function in a + // privileged scope, which we do using a sandbox. + var sysPrin = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(); + var sb = new SpecialPowers.Cu.Sandbox(sysPrin); + sb.usageHandler = usageHandler; + var cb = SpecialPowers.Cu.evalInSandbox((function(uri, usage, fileUsage) { + usageHandler(usage, fileUsage); }).toSource(), sb); + + let uri = SpecialPowers.wrap(window).document.documentURIObject; + quotaManager.getUsageForURI(uri, cb); } function getFileId(file) @@ -200,11 +212,6 @@ function getFileId(file) return utils.getFileId(file); } -function getFilePath(file) -{ - return utils.getFilePath(file); -} - function hasFileInfo(name, id) { return utils.getFileReferences(name, id); diff --git a/dom/indexedDB/test/helpers.js b/dom/indexedDB/test/helpers.js index 1e2841dadc8f..0a6a5f4b8b56 100644 --- a/dom/indexedDB/test/helpers.js +++ b/dom/indexedDB/test/helpers.js @@ -34,14 +34,42 @@ function executeSoon(aFun) } function clearAllDatabases(callback) { - let principal = SpecialPowers.wrap(document).nodePrincipal; - let appId, inBrowser; - if (principal.appId != Components.interfaces.nsIPrincipal.UNKNOWN_APP_ID && - principal.appId != Components.interfaces.nsIPrincipal.NO_APP_ID) { - appId = principal.appId; - inBrowser = principal.isInBrowserElement; + function runCallback() { + SimpleTest.executeSoon(function () { callback(); }); } - SpecialPowers.clearStorageForURI(document.documentURI, callback, appId, inBrowser); + + if (!SpecialPowers.isMainProcess()) { + runCallback(); + return; + } + + let comp = SpecialPowers.wrap(Components); + + let quotaManager = + comp.classes["@mozilla.org/dom/quota/manager;1"] + .getService(comp.interfaces.nsIQuotaManager); + + let uri = SpecialPowers.wrap(document).documentURIObject; + + // We need to pass a JS callback to getUsageForURI. However, that callback + // takes an XPCOM URI object, which will cause us to throw when we wrap it + // for the content compartment. So we need to define the function in a + // privileged scope, which we do using a sandbox. + var sysPrin = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(); + var sb = new SpecialPowers.Cu.Sandbox(sysPrin); + sb.ok = ok; + sb.runCallback = runCallback; + var cb = SpecialPowers.Cu.evalInSandbox((function(uri, usage, fileUsage) { + if (usage) { + ok(false, + "getUsageForURI returned non-zero usage after clearing all " + + "storages!"); + } + runCallback(); + }).toSource(), sb); + + quotaManager.clearStoragesForURI(uri); + quotaManager.getUsageForURI(uri, cb); } if (!window.runTest) { @@ -56,7 +84,6 @@ if (!window.runTest) { allowUnlimitedQuota(); } - enableTesting(); enableExperimental(); enableArchiveReader(); @@ -69,13 +96,13 @@ function finishTest() resetUnlimitedQuota(); resetExperimental(); resetArchiveReader(); - resetTesting(); SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", "free"); SimpleTest.executeSoon(function() { testGenerator.close(); - clearAllDatabases(function() { SimpleTest.finish(); }); + //clearAllDatabases(function() { SimpleTest.finish(); }); + SimpleTest.finish(); }); } @@ -197,6 +224,11 @@ function removePermission(type, url) SpecialPowers.removePermission(type, url); } +function setQuota(quota) +{ + SpecialPowers.setIntPref("dom.indexedDB.warningQuota", quota); +} + function allowUnlimitedQuota(url) { addPermission("indexedDB-unlimited", true, url); @@ -233,16 +265,6 @@ function resetExperimental() SpecialPowers.clearUserPref("dom.indexedDB.experimental"); } -function enableTesting() -{ - SpecialPowers.setBoolPref("dom.indexedDB.testing", true); -} - -function resetTesting() -{ - SpecialPowers.clearUserPref("dom.indexedDB.testing"); -} - function gc() { SpecialPowers.forceGC(); diff --git a/dom/indexedDB/test/mochitest.ini b/dom/indexedDB/test/mochitest.ini index 7701be78e71b..f8053fc50146 100644 --- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -14,346 +14,235 @@ support-files = leaving_page_iframe.html third_party_iframe1.html third_party_iframe2.html - unit/test_add_put.js - unit/test_add_twice_failure.js - unit/test_advance.js - unit/test_autoIncrement.js - unit/test_autoIncrement_indexes.js - unit/test_blocked_order.js - unit/test_clear.js - unit/test_complex_keyPaths.js - unit/test_count.js - unit/test_create_index.js - unit/test_create_index_with_integer_keys.js - unit/test_create_objectStore.js - unit/test_cursor_mutation.js - unit/test_cursor_update_updates_indexes.js - unit/test_cursors.js - unit/test_deleteDatabase.js - unit/test_deleteDatabase_interactions.js - unit/test_event_source.js - unit/test_getAll.js - unit/test_globalObjects_ipc.js - unit/test_globalObjects_other.js - unit/test_globalObjects_xpc.js - unit/test_global_data.js - unit/test_index_empty_keyPath.js - unit/test_index_getAll.js - unit/test_index_getAllObjects.js - unit/test_index_object_cursors.js - unit/test_index_update_delete.js - unit/test_indexes.js - unit/test_indexes_bad_values.js - unit/test_indexes_funny_things.js - unit/test_invalid_version.js - unit/test_invalidate.js - unit/test_key_requirements.js - unit/test_keys.js - unit/test_lowDiskSpace.js - unit/test_multientry.js - unit/test_names_sorted.js - unit/test_objectCursors.js - unit/test_objectStore_getAllKeys.js - unit/test_objectStore_inline_autoincrement_key_added_on_put.js - unit/test_objectStore_openKeyCursor.js - unit/test_objectStore_remove_values.js - unit/test_object_identity.js - unit/test_odd_result_order.js - unit/test_open_empty_db.js - unit/test_open_for_principal.js - unit/test_open_objectStore.js - unit/test_optionalArguments.js - unit/test_overlapping_transactions.js - unit/test_persistenceType.js - unit/test_put_get_values.js - unit/test_put_get_values_autoIncrement.js - unit/test_readonly_transactions.js - unit/test_remove_index.js - unit/test_remove_objectStore.js - unit/test_request_readyState.js - unit/test_setVersion.js - unit/test_setVersion_abort.js - unit/test_setVersion_events.js - unit/test_setVersion_exclusion.js - unit/test_success_events_after_abort.js - unit/test_temporary_storage.js - unit/test_traffic_jam.js - unit/test_transaction_abort.js - unit/test_transaction_abort_hang.js - unit/test_transaction_duplicate_store_names.js - unit/test_transaction_error.js - unit/test_transaction_lifetimes.js - unit/test_transaction_lifetimes_nested.js - unit/test_transaction_ordering.js - unit/test_unique_index_update.js - unit/test_writer_starvation.js webapp_clearBrowserData.js webapp_clearBrowserData_appFrame.html webapp_clearBrowserData_browserFrame.html [test_add_put.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_add_twice_failure.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_advance.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_app_isolation_inproc.html] -# The app isolation tests are only supposed to run in the main process. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage [test_app_isolation_oop.html] -# The app isolation tests are only supposed to run in the main process. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage [test_autoIncrement.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_autoIncrement_indexes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bfcache.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_blob_archive.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_blob_simple.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_blob_worker_crash.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 -[test_blocked_order.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 -[test_bug937006.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, bug 927889 still present [test_clear.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_complex_keyPaths.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_count.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_create_index.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_create_index_with_integer_keys.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_create_objectStore.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_cursor_mutation.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_cursor_update_updates_indexes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_cursors.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_deleteDatabase.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_deleteDatabase_interactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 -[test_disabled_quota_prompt.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_error_events_abort_transactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_event_propagation.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT, bug 780855 #Bug 931116, b2g desktop specific, initial triage [test_event_source.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_exceptions_in_events.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_array.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_cross_database_copying.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_os_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_put_get_object.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_put_get_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_quota.html] -# Quota handling disabled for now. -skip-if = true -# skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_replace.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_resurrection_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_resurrection_transaction_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_sharing.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_file_transaction_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_filehandle_append_read_data.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_compat.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_getFile.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_lifetimes.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_lifetimes_nested.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_location.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_ordering.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_overlapping.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_progress_events.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' # b2g(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) b2g-debug(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) b2g-desktop(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) [test_filehandle_quota.html] -# FileHandle is not supported in child processes. -# Quota handling disabled for now. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || true +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_filehandle_readonly_exceptions.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_request_readyState.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_serialization.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_filehandle_store_snapshot.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_filehandle_stream_tracking.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_success_events_after_abort.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_truncate.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_workers.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_filehandle_write_read_data.html] -# FileHandle is not supported in child processes. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +skip-if = buildapp == 'b2g' [test_getAll.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +[test_getFileId.html] [test_get_filehandle.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_globalObjects_content.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_global_data.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_index_empty_keyPath.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_index_getAll.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_index_getAllObjects.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_index_object_cursors.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_index_update_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_indexes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_indexes_bad_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_indexes_funny_things.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_invalid_version.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 -[test_invalidate.html] -# disabled for the moment -skip-if = true -# skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_key_requirements.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_keys.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_leaving_page.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_lowDiskSpace.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = buildapp == 'b2g' # b2g(this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions) b2g-debug(this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions) b2g-desktop(this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions) [test_multientry.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_names_sorted.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_objectCursors.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_objectStore_getAllKeys.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_objectStore_inline_autoincrement_key_added_on_put.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_objectStore_openKeyCursor.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_objectStore_remove_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_object_identity.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_odd_result_order.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_open_empty_db.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_open_for_principal.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_open_objectStore.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_optionalArguments.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_overlapping_transactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_persistenceType.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage [test_put_get_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_put_get_values_autoIncrement.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_readonly_transactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_remove_index.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_remove_objectStore.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_request_readyState.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_setVersion.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_setVersion_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_setVersion_events.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_setVersion_exclusion.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_success_events_after_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; time out) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) [test_third_party.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage [test_traffic_jam.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_abort_hang.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_duplicate_store_names.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_error.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_lifetimes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_lifetimes_nested.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_transaction_ordering.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_unique_index_update.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_webapp_clearBrowserData_inproc_inproc.html] -# The clearBrowserData tests are only supposed to run in the main process. -# They currently time out on android. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android' +skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #No test app installed #Bug 931116, b2g desktop specific, initial triage [test_webapp_clearBrowserData_inproc_oop.html] -# The clearBrowserData tests are only supposed to run in the main process. -# They currently time out on android. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android' +skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #No test app installed #Bug 931116, b2g desktop specific, initial triage [test_webapp_clearBrowserData_oop_inproc.html] -# The clearBrowserData tests are only supposed to run in the main process. -# They currently time out on android. -skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android' +skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #No test app installed #Bug 931116, b2g desktop specific, initial triage +[test_bug937006.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage diff --git a/dom/indexedDB/test/test_blob_simple.html b/dom/indexedDB/test/test_blob_simple.html index e7e4407197a9..854b90f06245 100644 --- a/dom/indexedDB/test/test_blob_simple.html +++ b/dom/indexedDB/test/test_blob_simple.html @@ -41,14 +41,11 @@ objectStore.add(data).onsuccess = grabEventAndContinueHandler; event = yield undefined; - info("Added blob to database once"); - let key = event.target.result; objectStore.add(data).onsuccess = grabEventAndContinueHandler; event = yield undefined; - info("Added blob to database twice"); info("Let's retrieve the blob again and verify the contents is the same."); @@ -56,8 +53,6 @@ objectStore.get(key).onsuccess = grabEventAndContinueHandler; event = yield undefined; - info("Got blob from database"); - let fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(event.target.result.blob); @@ -73,8 +68,6 @@ objectStore.get(key).onsuccess = grabEventAndContinueHandler; event = yield undefined; - info("Got blob from database"); - let blobURL = URL.createObjectURL(event.target.result.blob); let xhr = new XMLHttpRequest(); @@ -109,12 +102,10 @@ objectStore.openCursor().onsuccess = function(event) { let cursor = event.target.result; if (cursor) { - info("Got item from cursor"); cursorResults.push(cursor.value); cursor.continue(); } else { - info("Finished cursor"); continueToNextStep(); } }; @@ -136,8 +127,6 @@ index.get(INDEX_KEY).onsuccess = grabEventAndContinueHandler; event = yield undefined; - info("Got blob from database"); - fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(event.target.result.blob); @@ -164,12 +153,10 @@ index.openCursor().onsuccess = function(event) { let cursor = event.target.result; if (cursor) { - info("Got item from cursor"); cursorResults.push(cursor.value); cursor.continue(); } else { - info("Finished cursor"); continueToNextStep(); } }; @@ -239,15 +226,11 @@ event = yield undefined; let blobFromDB = event.target.result.blob; - info("Got blob from database"); - let txn = db.transaction("foo", "readwrite"); txn.objectStore("foo").put(event.target.result, key); txn.oncomplete = grabEventAndContinueHandler; event = yield undefined; - info("Stored blob back into database"); - fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(blobFromDB); diff --git a/dom/indexedDB/test/test_blocked_order.html b/dom/indexedDB/test/test_blocked_order.html deleted file mode 100644 index 9b82995a399a..000000000000 --- a/dom/indexedDB/test/test_blocked_order.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - IndexedDB Test - - - - - - - - - - - diff --git a/dom/indexedDB/test/test_disabled_quota_prompt.html b/dom/indexedDB/test/test_disabled_quota_prompt.html deleted file mode 100644 index 7565a73a6f3e..000000000000 --- a/dom/indexedDB/test/test_disabled_quota_prompt.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - Indexed Database Property Test - - - - - - - - - - - - - - diff --git a/dom/indexedDB/test/test_file_cross_database_copying.html b/dom/indexedDB/test/test_file_cross_database_copying.html index 7a66cb322e7f..69bbd74e7c5b 100644 --- a/dom/indexedDB/test/test_file_cross_database_copying.html +++ b/dom/indexedDB/test/test_file_cross_database_copying.html @@ -62,7 +62,8 @@ continue; } - isnot(getFilePath(result), getFilePath(refResult), "Different os files"); + isnot(SpecialPowers.getMozFullPath(result), + SpecialPowers.getMozFullPath(refResult), "Different os files"); } for (let i = 1; i < databases.length; i++) { @@ -85,7 +86,8 @@ verifyBlob(result, refResult, 2); yield undefined; - isnot(getFilePath(result), getFilePath(refResult), "Different os files"); + isnot(SpecialPowers.getMozFullPath(result), + SpecialPowers.getMozFullPath(refResult), "Different os files"); } is(bufferCache.length, 2, "Correct length"); diff --git a/dom/indexedDB/test/test_file_os_delete.html b/dom/indexedDB/test/test_file_os_delete.html index 6403f3f514ad..f24c793f010f 100644 --- a/dom/indexedDB/test/test_file_os_delete.html +++ b/dom/indexedDB/test/test_file_os_delete.html @@ -83,13 +83,6 @@ scheduleGC(); yield undefined; - // This isn't really necessary but in order to ensure that our files have - // been deleted we need to round-trip with the PBackground thread... - let request = indexedDB.deleteDatabase(name + "this can't exist"); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - yield undefined; - getUsage(grabFileUsageAndContinueHandler); let endUsage = yield undefined; diff --git a/dom/indexedDB/test/test_file_sharing.html b/dom/indexedDB/test/test_file_sharing.html index 2cb3f47544d1..1273f1f3073b 100644 --- a/dom/indexedDB/test/test_file_sharing.html +++ b/dom/indexedDB/test/test_file_sharing.html @@ -61,7 +61,8 @@ continue; } - is(getFilePath(result), getFilePath(refResult), "The same os file"); + is(SpecialPowers.getMozFullPath(result), + SpecialPowers.getMozFullPath(refResult), "The same os file"); } for (let i = 1; i < objectStoreInfo.length; i++) { @@ -84,7 +85,8 @@ verifyBlob(result, refResult, 1); yield undefined; - is(getFilePath(result), getFilePath(refResult), "The same os file"); + is(SpecialPowers.getMozFullPath(result), + SpecialPowers.getMozFullPath(refResult), "The same os file"); } is(bufferCache.length, 2, "Correct length"); diff --git a/dom/indexedDB/test/test_filehandle_compat.html b/dom/indexedDB/test/test_filehandle_compat.html index 028ebd5f5449..6b5bc15abfa6 100644 --- a/dom/indexedDB/test/test_filehandle_compat.html +++ b/dom/indexedDB/test/test_filehandle_compat.html @@ -40,7 +40,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_getFile.html b/dom/indexedDB/test/test_filehandle_getFile.html index 46e012a59dcd..eea32932bd38 100644 --- a/dom/indexedDB/test/test_filehandle_getFile.html +++ b/dom/indexedDB/test/test_filehandle_getFile.html @@ -44,7 +44,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_lifetimes.html b/dom/indexedDB/test/test_filehandle_lifetimes.html index 9a24632a4284..bb8ba8d7e9d8 100644 --- a/dom/indexedDB/test/test_filehandle_lifetimes.html +++ b/dom/indexedDB/test/test_filehandle_lifetimes.html @@ -48,7 +48,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html index 2db35eebfdb7..31b91fd7382d 100644 --- a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html +++ b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html @@ -60,7 +60,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_location.html b/dom/indexedDB/test/test_filehandle_location.html index 052c2ee52907..2b0682e49fd4 100644 --- a/dom/indexedDB/test/test_filehandle_location.html +++ b/dom/indexedDB/test/test_filehandle_location.html @@ -95,7 +95,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_ordering.html b/dom/indexedDB/test/test_filehandle_ordering.html index 71b181f61340..33f5e262f6ed 100644 --- a/dom/indexedDB/test/test_filehandle_ordering.html +++ b/dom/indexedDB/test/test_filehandle_ordering.html @@ -53,7 +53,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_overlapping.html b/dom/indexedDB/test/test_filehandle_overlapping.html index 6aaed5e376a9..75fca15f4cb5 100644 --- a/dom/indexedDB/test/test_filehandle_overlapping.html +++ b/dom/indexedDB/test/test_filehandle_overlapping.html @@ -64,7 +64,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html index d65656547b9f..3bb4f5e810ea 100644 --- a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html +++ b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html @@ -72,7 +72,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_request_readyState.html b/dom/indexedDB/test/test_filehandle_request_readyState.html index ff28544669a4..8fc19894a38c 100644 --- a/dom/indexedDB/test/test_filehandle_request_readyState.html +++ b/dom/indexedDB/test/test_filehandle_request_readyState.html @@ -60,7 +60,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html index 70b9acdbb691..9fd902c6abc3 100644 --- a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html +++ b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html @@ -65,7 +65,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_invalidate.html b/dom/indexedDB/test/test_invalidate.html deleted file mode 100644 index 45651953cb41..000000000000 --- a/dom/indexedDB/test/test_invalidate.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - IndexedDB Test - - - - - - - - - - - diff --git a/dom/indexedDB/test/test_persistenceType.html b/dom/indexedDB/test/test_persistenceType.html index 773a84d6569d..89e8162f6157 100644 --- a/dom/indexedDB/test/test_persistenceType.html +++ b/dom/indexedDB/test/test_persistenceType.html @@ -29,21 +29,10 @@ let request = indexedDB.open(name, { version: version, storage: "persistent" }); - request.onerror = grabEventAndContinueHandler; - request.onupgradeneeded = unexpectedSuccessHandler; - request.onsuccess = unexpectedSuccessHandler; - let event = yield undefined; - - is(event.type, "error", "Got error event"); - is(event.target, request, "Got correct target"); - is(event.target.error.name, "UnknownError", "Got correct error name"); - event.preventDefault(); - - request = indexedDB.open(name, { version: version }); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; request.onsuccess = grabEventAndContinueHandler; - event = yield undefined; + let event = yield undefined; is(event.type, "upgradeneeded", "Got correct event type"); diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/head.js similarity index 74% rename from dom/indexedDB/test/unit/xpcshell-head-parent-process.js rename to dom/indexedDB/test/unit/head.js index cc1e05b0ada4..1ec20b3463e0 100644 --- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js +++ b/dom/indexedDB/test/unit/head.js @@ -8,14 +8,17 @@ const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; const DOMException = Ci.nsIDOMDOMException; function is(a, b, msg) { + dump("is(" + a + ", " + b + ", \"" + msg + "\")"); do_check_eq(a, b, Components.stack.caller); } function ok(cond, msg) { + dump("ok(" + cond + ", \"" + msg + "\")"); do_check_true(!!cond, Components.stack.caller); } function isnot(a, b, msg) { + dump("isnot(" + a + ", " + b + ", \"" + msg + "\")"); do_check_neq(a, b, Components.stack.caller); } @@ -24,7 +27,7 @@ function executeSoon(fun) { } function todo(condition, name, diag) { - todo_check_true(condition, Components.stack.caller); + dump("TODO: ", diag); } function info(name, message) { @@ -38,13 +41,10 @@ function run_test() { if (!this.runTest) { this.runTest = function() { - if (SpecialPowers.isMainProcess()) { - // XPCShell does not get a profile by default. - do_get_profile(); + // XPCShell does not get a profile by default. + do_get_profile(); - enableTesting(); - enableExperimental(); - } + enableExperimental(); Cu.importGlobalProperties(["indexedDB"]); @@ -55,13 +55,9 @@ if (!this.runTest) { function finishTest() { - if (SpecialPowers.isMainProcess()) { - resetExperimental(); - resetTesting(); - - SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", - "free"); - } + resetExperimental(); + SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", + "free"); do_execute_soon(function(){ testGenerator.close(); @@ -83,11 +79,7 @@ function continueToNextStep() function errorHandler(event) { - try { - dump("indexedDB error: " + event.target.error.name); - } catch(e) { - dump("indexedDB error: " + e); - } + dump("indexedDB error: " + event.target.error.name); do_check_true(false); finishTest(); } @@ -166,6 +158,11 @@ function removePermission(permission, url) throw "removePermission"; } +function setQuota(quota) +{ + throw "setQuota"; +} + function allowIndexedDB(url) { throw "allowIndexedDB"; @@ -196,20 +193,10 @@ function resetExperimental() SpecialPowers.clearUserPref("dom.indexedDB.experimental"); } -function enableTesting() -{ - SpecialPowers.setBoolPref("dom.indexedDB.testing", true); -} - -function resetTesting() -{ - SpecialPowers.clearUserPref("dom.indexedDB.testing"); -} - function gc() { - Cu.forceGC(); - Cu.forceCC(); + Components.utils.forceGC(); + Components.utils.forceCC(); } function scheduleGC() @@ -230,42 +217,6 @@ function setTimeout(fun, timeout) { return timer; } -function clearAllDatabases(callback) { - if (!SpecialPowers.isMainProcess()) { - throw new Error("clearAllDatabases not implemented for child processes!"); - } - - let quotaManager = Cc["@mozilla.org/dom/quota/manager;1"] - .getService(Ci.nsIQuotaManager); - - const quotaPref = "dom.quotaManager.testing"; - - let oldPrefValue; - if (SpecialPowers._getPrefs().prefHasUserValue(quotaPref)) { - oldPrefValue = SpecialPowers.getBoolPref(quotaPref); - } - - SpecialPowers.setBoolPref(quotaPref, true); - - try { - quotaManager.clear(); - } catch(e) { - if (oldPrefValue !== undefined) { - SpecialPowers.setBoolPref(quotaPref, oldPrefValue); - } else { - SpecialPowers.clearUserPref(quotaPref); - } - throw e; - } - - let uri = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService) - .newURI("http://foo.com", null, null); - quotaManager.getUsageForURI(uri, function(usage, fileUsage) { - callback(); - }); -} - var SpecialPowers = { isMainProcess: function() { return Components.classes["@mozilla.org/xre/app-info;1"] diff --git a/dom/indexedDB/test/unit/xpcshell-shared.ini b/dom/indexedDB/test/unit/mochitest.ini similarity index 88% rename from dom/indexedDB/test/unit/xpcshell-shared.ini rename to dom/indexedDB/test/unit/mochitest.ini index 75ea49ade383..2cfaf2f670e4 100644 --- a/dom/indexedDB/test/unit/xpcshell-shared.ini +++ b/dom/indexedDB/test/unit/mochitest.ini @@ -1,20 +1,16 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. +[DEFAULT] [test_add_put.js] [test_add_twice_failure.js] [test_advance.js] [test_autoIncrement.js] [test_autoIncrement_indexes.js] -[test_blocked_order.js] [test_clear.js] [test_complex_keyPaths.js] [test_count.js] [test_create_index.js] [test_create_index_with_integer_keys.js] [test_create_objectStore.js] -[test_cursor_cycle.js] [test_cursor_mutation.js] [test_cursor_update_updates_indexes.js] [test_cursors.js] @@ -22,6 +18,7 @@ [test_deleteDatabase_interactions.js] [test_event_source.js] [test_getAll.js] +[test_globalObjects_ipc.js] [test_globalObjects_other.js] [test_globalObjects_xpc.js] [test_global_data.js] @@ -36,14 +33,15 @@ [test_invalid_version.js] [test_key_requirements.js] [test_keys.js] +[test_lowDiskSpace.js] [test_multientry.js] [test_names_sorted.js] -[test_object_identity.js] [test_objectCursors.js] [test_objectStore_getAllKeys.js] [test_objectStore_inline_autoincrement_key_added_on_put.js] [test_objectStore_openKeyCursor.js] [test_objectStore_remove_values.js] +[test_object_identity.js] [test_odd_result_order.js] [test_open_empty_db.js] [test_open_for_principal.js] @@ -62,6 +60,7 @@ [test_setVersion_events.js] [test_setVersion_exclusion.js] [test_success_events_after_abort.js] +[test_temporary_storage.js] [test_traffic_jam.js] [test_transaction_abort.js] [test_transaction_abort_hang.js] diff --git a/dom/indexedDB/test/unit/test_blocked_order.js b/dom/indexedDB/test/unit/test_blocked_order.js deleted file mode 100644 index 3e54ccece6ca..000000000000 --- a/dom/indexedDB/test/unit/test_blocked_order.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -let testGenerator = testSteps(); - -function testSteps() -{ - const databaseName = - ("window" in this) ? window.location.pathname : "Test"; - const databaseCount = 10; - - // Test 1: Make sure basic versionchange events work and that they don't - // trigger blocked events. - info("Opening " + databaseCount + " databases with version 1"); - - let databases = []; - - for (let i = 0; i < databaseCount; i++) { - let thisIndex = i; - - info("Opening database " + thisIndex); - - let request = indexedDB.open(databaseName, 1); - request.onerror = errorHandler; - request.onblocked = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - - let event = yield undefined; - - is(event.type, "success", "Got success event"); - - let db = request.result; - is(db.version, 1, "Got version 1"); - - db.onversionchange = function(event) { - info("Closing database " + thisIndex); - db.close(); - - databases.splice(databases.indexOf(db), 1); - }; - - databases.push(db); - } - - is(databases.length, databaseCount, "Created all databases with version 1"); - - info("Opening database with version 2"); - - let request = indexedDB.open(databaseName, 2); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - - request.onblocked = function(event) { - ok(false, "Should not receive a blocked event"); - }; - - event = yield undefined; - - is(event.type, "success", "Got success event"); - is(databases.length, 0, "All databases with version 1 were closed"); - - let db = request.result; - is(db.version, 2, "Got version 2"); - - info("Deleting database with version 2"); - db.close(); - - request = indexedDB.deleteDatabase(databaseName); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - - event = yield undefined; - - is(event.type, "success", "Got success event"); - - // Test 2: Make sure blocked events aren't delivered until all versionchange - // events have been delivered. - info("Opening " + databaseCount + " databases with version 1"); - - for (let i = 0; i < databaseCount; i++) { - let thisIndex = i; - - info("Opening database " + thisIndex); - - let request = indexedDB.open(databaseName, 1); - request.onerror = errorHandler; - request.onblocked = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - - let event = yield undefined; - - is(event.type, "success", "Got success event"); - - let db = request.result; - is(db.version, 1, "Got version 1"); - - db.onversionchange = function(event) { - if (thisIndex == (databaseCount - 1)) { - info("Closing all databases with version 1"); - - for (let j = 0; j < databases.length; j++) { - databases[j].close(); - } - - databases = []; - info("Done closing all databases with version 1"); - } else { - info("Not closing database " + thisIndex); - } - }; - - databases.push(db); - } - - is(databases.length, databaseCount, "Created all databases with version 1"); - - info("Opening database with version 2"); - - request = indexedDB.open(databaseName, 2); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - - request.onblocked = function(event) { - ok(false, "Should not receive a blocked event"); - }; - - event = yield undefined; - - is(event.type, "success", "Got success event"); - is(databases.length, 0, "All databases with version 1 were closed"); - - db = request.result; - is(db.version, 2, "Got version 2"); - - info("Deleting database with version 2"); - db.close(); - - request = indexedDB.deleteDatabase(databaseName); - request.onerror = errorHandler; - request.onsuccess = grabEventAndContinueHandler; - - event = yield undefined; - - is(event.type, "success", "Got success event"); - - finishTest(); - yield undefined; -} diff --git a/dom/indexedDB/test/unit/test_indexes.js b/dom/indexedDB/test/unit/test_indexes.js index aaf536febe4d..f1fd7b261a69 100644 --- a/dom/indexedDB/test/unit/test_indexes.js +++ b/dom/indexedDB/test/unit/test_indexes.js @@ -97,7 +97,7 @@ function testSteps() is(found, true, "objectStore has our index"); let index = objectStore.index(indexData[i].name); is(index.name, indexData[i].name, "Correct name"); - is(index.objectStore.name, objectStore.name, "Correct store name"); + is(index.storeName, objectStore.name, "Correct store name"); is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); is(index.unique, indexData[i].options.unique ? true : false, "Correct unique value"); diff --git a/dom/indexedDB/test/unit/test_indexes_funny_things.js b/dom/indexedDB/test/unit/test_indexes_funny_things.js index 33386e079f97..a44f8eb18401 100644 --- a/dom/indexedDB/test/unit/test_indexes_funny_things.js +++ b/dom/indexedDB/test/unit/test_indexes_funny_things.js @@ -93,7 +93,7 @@ function testSteps() is(found, true, "objectStore has our index"); let index = objectStore.index(indexData[i].name); is(index.name, indexData[i].name, "Correct name"); - is(index.objectStore.name, objectStore.name, "Correct store name"); + is(index.storeName, objectStore.name, "Correct store name"); is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); is(index.unique, indexData[i].options.unique ? true : false, "Correct unique value"); diff --git a/dom/indexedDB/test/unit/test_invalidate.js b/dom/indexedDB/test/unit/test_invalidate.js deleted file mode 100644 index fe34bb015966..000000000000 --- a/dom/indexedDB/test/unit/test_invalidate.js +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -let testGenerator = testSteps(); - -function testSteps() -{ - const databaseName = - ("window" in this) ? window.location.pathname : "Test"; - - let dbCount = 0; - - // Test invalidating during a versionchange transaction. - info("Opening database " + ++dbCount); - - let request = indexedDB.open(databaseName, dbCount); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - let event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database " + dbCount); - - request.onupgradeneeded = unexpectedSuccessHandler; - - let objStore = - request.result.createObjectStore("foo", { autoIncrement: true }); - objStore.createIndex("fooIndex", "fooIndex", { unique: true }); - objStore.put({ foo: 1 }); - objStore.get(1); - objStore.count(); - objStore.openCursor(); - objStore.delete(1); - - info("Invalidating database " + dbCount); - - clearAllDatabases(continueToNextStepSync); - - objStore = request.result.createObjectStore("bar"); - objStore.createIndex("barIndex", "barIndex", { multiEntry: true }); - objStore.put({ bar: 1, barIndex: [ 0, 1 ] }, 10); - objStore.get(10); - objStore.count(); - objStore.openCursor(); - objStore.delete(10); - - yield undefined; - - executeSoon(continueToNextStepSync); - yield undefined; - - // Test invalidating after the complete event of a versionchange transaction. - info("Opening database " + ++dbCount); - - request = indexedDB.open(databaseName, dbCount); - request.onerror = errorHandler; - request.onupgradeneeded = grabEventAndContinueHandler; - request.onsuccess = unexpectedSuccessHandler; - event = yield undefined; - - is(event.type, "upgradeneeded", "Upgrading database " + dbCount); - - request.onupgradeneeded = unexpectedSuccessHandler; - - request.transaction.oncomplete = grabEventAndContinueHandler; - event = yield undefined; - - is(event.type, "complete", - "Got complete event for versionchange transaction on database " + dbCount); - - info("Invalidating database " + dbCount); - - clearAllDatabases(continueToNextStepSync); - yield undefined; - - executeSoon(continueToNextStepSync); - - finishTest(); - yield undefined; -} diff --git a/dom/indexedDB/test/unit/test_setVersion_events.js b/dom/indexedDB/test/unit/test_setVersion_events.js index 7560d1fa2cba..14dc55ff17df 100644 --- a/dom/indexedDB/test/unit/test_setVersion_events.js +++ b/dom/indexedDB/test/unit/test_setVersion_events.js @@ -15,12 +15,12 @@ function testSteps() // Sanity checks ok(request instanceof IDBRequest, "Request should be an IDBRequest"); ok(request instanceof IDBOpenDBRequest, "Request should be an IDBOpenDBRequest"); - ok(request instanceof EventTarget, "Request should be an EventTarget"); + //ok(request instanceof EventTarget, "Request should be an EventTarget"); is(request.source, null, "Request should have no source"); try { request.result; ok(false, "Getter should have thrown!"); - } catch (e if e.result == 0x8053000b /* NS_ERROR_DOM_INVALID_STATE_ERR */) { + } catch (e if e.result == 0x80660006 /* NS_ERROR_DOM_INDEXEDDB_NOTALLOWED_ERR */) { ok(true, "Getter threw the right exception"); } diff --git a/dom/indexedDB/test/unit/test_temporary_storage.js b/dom/indexedDB/test/unit/test_temporary_storage.js index 671c8771f3fb..cb8fd9090fd8 100644 --- a/dom/indexedDB/test/unit/test_temporary_storage.js +++ b/dom/indexedDB/test/unit/test_temporary_storage.js @@ -10,15 +10,15 @@ function testSteps() const name = this.window ? window.location.pathname : "Splendid Test"; const urls = [ - { url: "http://www.alpha.com", flags: [true, true, true, true] }, + { url: "http://www.alpha.com", flags: [true, true, false, false] }, { url: "http://www.beta.com", flags: [true, false, false, false] }, { url: "http://www.gamma.com", flags: [true, true, false, false] }, { url: "http://www.delta.com", flags: [true, true, false, false] }, { url: "http://www.epsilon.com", flags: [true, true, false, false] }, { url: "http://www2.alpha.com", flags: [true, true, false, false] }, { url: "http://www2.beta.com", flags: [true, true, false, false] }, - { url: "http://www2.gamma.com", flags: [true, true, false, false] }, - { url: "http://www2.delta.com", flags: [true, true, true, false] }, + { url: "http://www2.gamma.com", flags: [true, true, true, false] }, + { url: "http://www2.delta.com", flags: [true, true, true, true] }, { url: "http://www2.epsilon.com", flags: [true, true, true, true] }, { url: "http://joe.blog.alpha.com", flags: [true, true, true, true] }, { url: "http://joe.blog.beta.com", flags: [true, true, true, true] }, @@ -78,14 +78,13 @@ function testSteps() let handledIndex = 0; function usageHandler(usage, fileUsage) { - let data = urls[handledIndex++]; - if (data.flags[stageIndex - 1]) { - ok(usage > 0, "Non-zero usage for '" + data.url + "'"); + if (urls[handledIndex].flags[stageIndex - 1]) { + ok(usage > 0, "Correct usage"); } else { - ok(usage == 0, "Zero usage for '" + data.url + "'"); + ok(usage == 0, "Correct usage"); } - if (handledIndex == urls.length) { + if (++handledIndex == urls.length) { continueToNextStep(); } } @@ -113,13 +112,10 @@ function testSteps() setLimit(lastIndex * dbSize / 1024); quotaManager.clear(); - info("Stage 1"); - + // Stage 1 for (let i = 0; i < lastIndex; i++) { let data = urls[i]; - info("Opening database for " + data.url); - request = indexedDB.openForPrincipal(getPrincipal(data.url), name, { storage: "temporary" }); request.onerror = errorHandler; @@ -148,8 +144,7 @@ function testSteps() checkUsage(1); yield undefined; - info("Stage 2"); - + // Stage 2 for (let i = 1; i < urls.length; i++) { databases[i] = null; @@ -187,8 +182,7 @@ function testSteps() checkUsage(2); yield undefined; - info("Stage 3"); - + // Stage 3 setLimit(14 * dbSize / 1024); quotaManager.reset(); @@ -205,8 +199,7 @@ function testSteps() checkUsage(3); yield undefined; - info("Stage 4"); - + // Stage 4 let trans = db.transaction(["foo"], "readwrite"); let blob = Blob(["bar"]); @@ -221,8 +214,7 @@ function testSteps() checkUsage(4); yield undefined; - info("Cleanup"); - + // Cleanup setLimit(); quotaManager.reset(); diff --git a/dom/indexedDB/test/unit/xpcshell-child-process.ini b/dom/indexedDB/test/unit/xpcshell-child-process.ini deleted file mode 100644 index d9a7b7589c9e..000000000000 --- a/dom/indexedDB/test/unit/xpcshell-child-process.ini +++ /dev/null @@ -1,18 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -[DEFAULT] -dupe-manifest = -head = xpcshell-head-child-process.js -tail = -support-files = - GlobalObjectsChild.js - GlobalObjectsComponent.js - GlobalObjectsComponent.manifest - GlobalObjectsModule.jsm - GlobalObjectsSandbox.js - xpcshell-head-parent-process.js - xpcshell-shared.ini - -[include:xpcshell-shared.ini] diff --git a/dom/indexedDB/test/unit/xpcshell-head-child-process.js b/dom/indexedDB/test/unit/xpcshell-head-child-process.js deleted file mode 100644 index 2e704f8dc091..000000000000 --- a/dom/indexedDB/test/unit/xpcshell-head-child-process.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -function run_test() { - const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; - - const INDEXEDDB_HEAD_FILE = "xpcshell-head-parent-process.js"; - const INDEXEDDB_PREF_EXPERIMENTAL = "dom.indexedDB.experimental"; - - // IndexedDB needs a profile. - do_get_profile(); - - let thisTest = _TEST_FILE.toString().replace(/\\/g, "/"); - thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1); - - _HEAD_FILES.push(do_get_file(INDEXEDDB_HEAD_FILE).path.replace(/\\/g, "/")); - - - let prefs = - Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService) - .getBranch(null); - prefs.setBoolPref(INDEXEDDB_PREF_EXPERIMENTAL, true); - - run_test_in_child(thisTest); -} diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini deleted file mode 100644 index cb7dc0e2eb96..000000000000 --- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini +++ /dev/null @@ -1,26 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -[DEFAULT] -dupe-manifest = -head = xpcshell-head-parent-process.js -tail = -support-files = - GlobalObjectsChild.js - GlobalObjectsComponent.js - GlobalObjectsComponent.manifest - GlobalObjectsModule.jsm - GlobalObjectsSandbox.js - xpcshell-shared.ini - -[include:xpcshell-shared.ini] - -[test_globalObjects_ipc.js] -[test_invalidate.js] -# disabled for the moment. -skip-if = true -[test_lowDiskSpace.js] -[test_temporary_storage.js] -# bug 951017: intermittent failure on Android x86 emulator -skip-if = os == "android" && processor == "x86" diff --git a/dom/indexedDB/test/unit/xpcshell.ini b/dom/indexedDB/test/unit/xpcshell.ini new file mode 100644 index 000000000000..306bd42e58cf --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell.ini @@ -0,0 +1,90 @@ +[DEFAULT] +head = head.js +tail = +support-files = + GlobalObjectsChild.js + GlobalObjectsComponent.js + GlobalObjectsComponent.manifest + GlobalObjectsModule.jsm + GlobalObjectsSandbox.js + +# When adding files here please also update ipc/unit/xpcshell.ini! + +[test_add_put.js] +[test_add_twice_failure.js] +[test_advance.js] +[test_autoIncrement.js] +[test_autoIncrement_indexes.js] +[test_clear.js] +[test_complex_keyPaths.js] +[test_count.js] +[test_create_index.js] +[test_create_index_with_integer_keys.js] +[test_create_objectStore.js] +[test_cursor_cycle.js] +[test_cursor_mutation.js] +[test_cursor_update_updates_indexes.js] +[test_cursors.js] +[test_deleteDatabase.js] +[test_deleteDatabase_interactions.js] +[test_event_source.js] +[test_getAll.js] +[test_globalObjects_ipc.js] +# FIXME/bug 575918: out-of-process xpcshell is broken on OS X +skip-if = buildapp == 'mulet' || os == "mac" || os == "android" +[test_globalObjects_other.js] +[test_globalObjects_xpc.js] +[test_global_data.js] +[test_index_empty_keyPath.js] +[test_index_getAll.js] +[test_index_getAllObjects.js] +[test_index_object_cursors.js] +[test_index_update_delete.js] +[test_indexes.js] +[test_indexes_bad_values.js] +[test_indexes_funny_things.js] +[test_invalid_version.js] +[test_key_requirements.js] +[test_keys.js] +[test_lowDiskSpace.js] +[test_multientry.js] +[test_names_sorted.js] +[test_object_identity.js] +[test_objectCursors.js] +[test_objectStore_getAllKeys.js] +[test_objectStore_inline_autoincrement_key_added_on_put.js] +[test_objectStore_openKeyCursor.js] +[test_objectStore_remove_values.js] +[test_odd_result_order.js] +[test_open_empty_db.js] +[test_open_for_principal.js] +[test_open_objectStore.js] +[test_optionalArguments.js] +[test_overlapping_transactions.js] +[test_persistenceType.js] +[test_put_get_values.js] +[test_put_get_values_autoIncrement.js] +[test_readonly_transactions.js] +[test_remove_index.js] +[test_remove_objectStore.js] +[test_request_readyState.js] +[test_setVersion.js] +[test_setVersion_abort.js] +[test_setVersion_events.js] +[test_setVersion_exclusion.js] +[test_success_events_after_abort.js] +[test_temporary_storage.js] +# bug 951017: intermittent failure on Android x86 emulator +skip-if = os == "android" && processor == "x86" +[test_traffic_jam.js] +[test_transaction_abort.js] +[test_transaction_abort_hang.js] +[test_transaction_duplicate_store_names.js] +[test_transaction_error.js] +[test_transaction_lifetimes.js] +[test_transaction_lifetimes_nested.js] +[test_transaction_ordering.js] +[test_unique_index_update.js] +[test_writer_starvation.js] + +# When adding files here please also update ipc/unit/xpcshell.ini! diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 53e064f010fa..f7acd3f8dacd 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -51,7 +51,7 @@ interface nsITranslationNodeList; interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; -[scriptable, uuid(ed12c067-506b-4b10-9853-d4bba5c1bb6c)] +[scriptable, uuid(669095d8-1b96-472f-a48d-022adde26cfd)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1473,13 +1473,6 @@ interface nsIDOMWindowUtils : nsISupports { */ [implicit_jscontext] long long getFileId(in jsval aFile); - /** - * Get internal file path of the stored file or file handle. - * - * TODO: File handle objects are actually not supported at the moment. - */ - [implicit_jscontext] AString getFilePath(in jsval aFile); - /** * Get file ref count info for given database and file id. * diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index e975569e6ca6..20bf89e4c583 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -2,10 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "BlobChild.h" -#include "BlobParent.h" +#include "Blob.h" -#include "BackgroundParent.h" #include "ContentChild.h" #include "ContentParent.h" #include "FileDescriptorSetChild.h" @@ -18,11 +16,8 @@ #include "mozilla/dom/nsIContentChild.h" #include "mozilla/dom/PBlobStreamChild.h" #include "mozilla/dom/PBlobStreamParent.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/PFileDescriptorSetParent.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "mozilla/ipc/PBackgroundChild.h" -#include "mozilla/ipc/PBackgroundParent.h" -#include "mozilla/ipc/PFileDescriptorSetParent.h" #include "nsCOMPtr.h" #include "nsDOMFile.h" #include "nsIDOMFile.h" @@ -32,20 +27,8 @@ #include "nsIRemoteBlob.h" #include "nsISeekableStream.h" #include "nsNetCID.h" +#include "nsProxyRelease.h" #include "nsThreadUtils.h" -#include "nsXULAppAPI.h" - -#ifdef DEBUG -#include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread(). -#endif - -#define DISABLE_ASSERTS_FOR_FUZZING 0 - -#if DISABLE_ASSERTS_FOR_FUZZING -#define ASSERT_UNLESS_FUZZING(...) do { } while (0) -#else -#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) -#endif #define PRIVATE_REMOTE_INPUT_STREAM_IID \ {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}} @@ -62,138 +45,21 @@ enum ActorType ParentActor }; -template -struct ConcreteManagerTypeTraits; - -template <> -struct ConcreteManagerTypeTraits -{ - typedef ContentChild Type; -}; - -template <> -struct ConcreteManagerTypeTraits -{ - typedef PBackgroundChild Type; -}; - -template <> -struct ConcreteManagerTypeTraits -{ - typedef ContentParent Type; -}; - -template <> -struct ConcreteManagerTypeTraits -{ - typedef PBackgroundParent Type; -}; - -template -void AssertCorrectThreadForManager(ManagerType* aManager); - -template <> -void AssertCorrectThreadForManager(nsIContentChild* aManager) -{ - MOZ_ASSERT(NS_IsMainThread()); -} - -template <> -void AssertCorrectThreadForManager(nsIContentParent* aManager) -{ - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - MOZ_ASSERT(NS_IsMainThread()); -} - -template <> -void AssertCorrectThreadForManager(PBackgroundChild* aManager) -{ -#ifdef DEBUG - if (aManager) { - PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread(); - MOZ_ASSERT(backgroundChild); - MOZ_ASSERT(backgroundChild == aManager); - } -#endif -} - -template <> -void AssertCorrectThreadForManager( - PBackgroundParent* aManager) -{ - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); - AssertIsOnBackgroundThread(); -} - -bool -EventTargetIsOnCurrentThread(nsIEventTarget* aEventTarget) -{ - if (!aEventTarget) { - return NS_IsMainThread(); - } - - bool current; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aEventTarget->IsOnCurrentThread(¤t))); - - return current; -} - -// Ensure that a nsCOMPtr/nsRefPtr is released on the target thread. +// Ensure that a nsCOMPtr/nsRefPtr is released on the main thread. template