summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/ActorsChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/ActorsChild.cpp')
-rw-r--r--dom/indexedDB/ActorsChild.cpp3588
1 files changed, 3588 insertions, 0 deletions
diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp
new file mode 100644
index 000000000..3e8f97348
--- /dev/null
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -0,0 +1,3588 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "ActorsChild.h"
+
+#include "BackgroundChildImpl.h"
+#include "IDBDatabase.h"
+#include "IDBEvents.h"
+#include "IDBFactory.h"
+#include "IDBIndex.h"
+#include "IDBMutableFile.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/TypeTraits.h"
+#include "mozilla/dom/Element.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 "nsIFileStreams.h"
+#include "nsNetCID.h"
+#include "nsPIDOMWindow.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "PermissionRequestBase.h"
+#include "ProfilerHelpers.h"
+#include "ReportInternalError.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.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 {
+
+using ipc::PrincipalInfo;
+
+namespace dom {
+
+using namespace workers;
+
+namespace indexedDB {
+
+/*******************************************************************************
+ * ThreadLocal
+ ******************************************************************************/
+
+ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId)
+ : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1)
+ , mCurrentTransaction(0)
+#ifdef DEBUG
+ , mOwningThread(PR_GetCurrentThread())
+#endif
+{
+ MOZ_ASSERT(mOwningThread);
+
+ MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal);
+
+ // NSID_LENGTH counts the null terminator, SetLength() does not.
+ mLoggingIdString.SetLength(NSID_LENGTH - 1);
+
+ aBackgroundChildLoggingId.ToProvidedString(
+ *reinterpret_cast<char(*)[NSID_LENGTH]>(mLoggingIdString.BeginWriting()));
+}
+
+ThreadLocal::~ThreadLocal()
+{
+ MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal);
+}
+
+#ifdef DEBUG
+
+void
+ThreadLocal::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
+#endif // DEBUG
+
+/*******************************************************************************
+ * 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 final
+{
+ typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
+
+ IDBTransaction* const mTransaction;
+ IDBTransaction* mPreviousTransaction;
+ ThreadLocal* mThreadLocal;
+
+public:
+ explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction)
+ : mTransaction(aTransaction)
+ , mPreviousTransaction(nullptr)
+ , mThreadLocal(nullptr)
+ {
+ if (aTransaction) {
+ BackgroundChildImpl::ThreadLocal* threadLocal =
+ BackgroundChildImpl::GetThreadLocalForCurrentThread();
+ MOZ_ASSERT(threadLocal);
+
+ // Hang onto this for resetting later.
+ mThreadLocal = threadLocal->mIndexedDBThreadLocal;
+ MOZ_ASSERT(mThreadLocal);
+
+ // Save the current value.
+ mPreviousTransaction = mThreadLocal->GetCurrentTransaction();
+
+ // Set the new value.
+ mThreadLocal->SetCurrentTransaction(aTransaction);
+ }
+ }
+
+ ~AutoSetCurrentTransaction()
+ {
+ MOZ_ASSERT_IF(mThreadLocal, mTransaction);
+ MOZ_ASSERT_IF(mThreadLocal,
+ mThreadLocal->GetCurrentTransaction() == mTransaction);
+
+ if (mThreadLocal) {
+ // Reset old value.
+ mThreadLocal->SetCurrentTransaction(mPreviousTransaction);
+ }
+ }
+
+ IDBTransaction*
+ Transaction() const
+ {
+ return mTransaction;
+ }
+};
+
+class MOZ_STACK_CLASS ResultHelper final
+ : public IDBRequest::ResultCallback
+{
+ IDBRequest* mRequest;
+ AutoSetCurrentTransaction mAutoTransaction;
+
+ union
+ {
+ IDBDatabase* mDatabase;
+ IDBCursor* mCursor;
+ IDBMutableFile* mMutableFile;
+ StructuredCloneReadInfo* mStructuredClone;
+ const nsTArray<StructuredCloneReadInfo>* mStructuredCloneArray;
+ const Key* mKey;
+ const nsTArray<Key>* mKeyArray;
+ const JS::Value* mJSVal;
+ const JS::Handle<JS::Value>* mJSValHandle;
+ } mResult;
+
+ enum
+ {
+ ResultTypeDatabase,
+ ResultTypeCursor,
+ ResultTypeMutableFile,
+ ResultTypeStructuredClone,
+ ResultTypeStructuredCloneArray,
+ ResultTypeKey,
+ ResultTypeKeyArray,
+ ResultTypeJSVal,
+ ResultTypeJSValHandle,
+ } mResultType;
+
+public:
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ IDBDatabase* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeDatabase)
+ {
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(aResult);
+
+ mResult.mDatabase = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ IDBCursor* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeCursor)
+ {
+ MOZ_ASSERT(aRequest);
+
+ mResult.mCursor = aResult;
+ }
+
+ ResultHelper(IDBRequest* aRequest,
+ IDBTransaction* aTransaction,
+ IDBMutableFile* aResult)
+ : mRequest(aRequest)
+ , mAutoTransaction(aTransaction)
+ , mResultType(ResultTypeMutableFile)
+ {
+ MOZ_ASSERT(aRequest);
+
+ mResult.mMutableFile = 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<StructuredCloneReadInfo>* 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<Key>* 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<JS::Value>* 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<JS::Value> aResult) override
+ {
+ MOZ_ASSERT(aCx);
+ MOZ_ASSERT(mRequest);
+
+ switch (mResultType) {
+ case ResultTypeDatabase:
+ return GetResult(aCx, mResult.mDatabase, aResult);
+
+ case ResultTypeCursor:
+ return GetResult(aCx, mResult.mCursor, aResult);
+
+ case ResultTypeMutableFile:
+ return GetResult(aCx, mResult.mMutableFile, 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:
+ template <class T>
+ typename EnableIf<IsSame<T, IDBDatabase>::value ||
+ IsSame<T, IDBCursor>::value ||
+ IsSame<T, IDBMutableFile>::value,
+ nsresult>::Type
+ GetResult(JSContext* aCx,
+ T* aDOMObject,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ if (!aDOMObject) {
+ aResult.setNull();
+ return NS_OK;
+ }
+
+ bool ok = GetOrCreateDOMReflector(aCx, aDOMObject, aResult);
+ if (NS_WARN_IF(!ok)) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ return NS_OK;
+ }
+
+ nsresult
+ GetResult(JSContext* aCx,
+ StructuredCloneReadInfo* aCloneInfo,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult);
+
+ if (NS_WARN_IF(!ok)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+
+ return NS_OK;
+ }
+
+ nsresult
+ GetResult(JSContext* aCx,
+ const nsTArray<StructuredCloneReadInfo>* aCloneInfos,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ JS::Rooted<JSObject*> 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<StructuredCloneReadInfo&>(aCloneInfos->ElementAt(index));
+
+ JS::Rooted<JS::Value> value(aCx);
+
+ nsresult rv = GetResult(aCx, &cloneInfo, &value);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!JS_DefineElement(aCx, array, index, value,
+ JSPROP_ENUMERATE))) {
+ 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<JS::Value> 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<Key>* aKeys,
+ JS::MutableHandle<JS::Value> aResult)
+ {
+ JS::Rooted<JSObject*> 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<JS::Value> value(aCx);
+
+ nsresult rv = GetResult(aCx, &key, &value);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!JS_DefineElement(aCx, array, index, value,
+ JSPROP_ENUMERATE))) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+ }
+ }
+
+ aResult.setObject(*array);
+ return NS_OK;
+ }
+};
+
+class PermissionRequestMainProcessHelper final
+ : public PermissionRequestBase
+{
+ BackgroundFactoryRequestChild* mActor;
+ RefPtr<IDBFactory> mFactory;
+
+public:
+ PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor,
+ IDBFactory* aFactory,
+ Element* aOwnerElement,
+ nsIPrincipal* aPrincipal)
+ : PermissionRequestBase(aOwnerElement, aPrincipal)
+ , mActor(aActor)
+ , mFactory(aFactory)
+ {
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aFactory);
+ aActor->AssertIsOnOwningThread();
+ }
+
+protected:
+ ~PermissionRequestMainProcessHelper()
+ { }
+
+private:
+ virtual void
+ OnPromptComplete(PermissionValue aPermissionValue) override;
+};
+
+class PermissionRequestChildProcessActor final
+ : public PIndexedDBPermissionRequestChild
+{
+ BackgroundFactoryRequestChild* mActor;
+ RefPtr<IDBFactory> 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) override;
+};
+
+void
+DeserializeStructuredCloneFiles(
+ IDBDatabase* aDatabase,
+ const nsTArray<SerializedStructuredCloneFile>& aSerializedFiles,
+ const nsTArray<RefPtr<JS::WasmModule>>* aModuleSet,
+ nsTArray<StructuredCloneFile>& aFiles)
+{
+ MOZ_ASSERT_IF(aModuleSet, !aModuleSet->IsEmpty());
+ MOZ_ASSERT(aFiles.IsEmpty());
+
+ if (!aSerializedFiles.IsEmpty()) {
+ uint32_t moduleIndex = 0;
+
+ const uint32_t count = aSerializedFiles.Length();
+ aFiles.SetCapacity(count);
+
+ for (uint32_t index = 0; index < count; index++) {
+ const SerializedStructuredCloneFile& serializedFile =
+ aSerializedFiles[index];
+
+ const BlobOrMutableFile& blobOrMutableFile = serializedFile.file();
+
+ switch (serializedFile.type()) {
+ case StructuredCloneFile::eBlob: {
+ MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::TPBlobChild);
+
+ auto* actor =
+ static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild());
+
+ RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
+ MOZ_ASSERT(blobImpl);
+
+ RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
+
+ aDatabase->NoteReceivedBlob(blob);
+
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mType = StructuredCloneFile::eBlob;
+ file->mBlob.swap(blob);
+
+ break;
+ }
+
+ case StructuredCloneFile::eMutableFile: {
+ MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t ||
+ blobOrMutableFile.type() ==
+ BlobOrMutableFile::TPBackgroundMutableFileChild);
+
+ switch (blobOrMutableFile.type()) {
+ case BlobOrMutableFile::Tnull_t: {
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mType = StructuredCloneFile::eMutableFile;
+
+ break;
+ }
+
+ case BlobOrMutableFile::TPBackgroundMutableFileChild: {
+ auto* actor =
+ static_cast<BackgroundMutableFileChild*>(
+ blobOrMutableFile.get_PBackgroundMutableFileChild());
+ MOZ_ASSERT(actor);
+
+ actor->EnsureDOMObject();
+
+ auto* mutableFile =
+ static_cast<IDBMutableFile*>(actor->GetDOMObject());
+ MOZ_ASSERT(mutableFile);
+
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mType = StructuredCloneFile::eMutableFile;
+ file->mMutableFile = mutableFile;
+
+ actor->ReleaseDOMObject();
+
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+
+ break;
+ }
+
+ case StructuredCloneFile::eStructuredClone: {
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mType = StructuredCloneFile::eStructuredClone;
+
+ break;
+ }
+
+ case StructuredCloneFile::eWasmBytecode:
+ case StructuredCloneFile::eWasmCompiled: {
+ if (aModuleSet) {
+ MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
+
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mType = serializedFile.type();
+
+ MOZ_ASSERT(moduleIndex < aModuleSet->Length());
+ file->mWasmModule = aModuleSet->ElementAt(moduleIndex);
+
+ if (serializedFile.type() == StructuredCloneFile::eWasmCompiled) {
+ moduleIndex++;
+ }
+
+ break;
+ }
+
+ MOZ_ASSERT(blobOrMutableFile.type() ==
+ BlobOrMutableFile::TPBlobChild);
+
+ auto* actor =
+ static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild());
+
+ RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
+ MOZ_ASSERT(blobImpl);
+
+ RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
+
+ aDatabase->NoteReceivedBlob(blob);
+
+ StructuredCloneFile* file = aFiles.AppendElement();
+ MOZ_ASSERT(file);
+
+ file->mType = serializedFile.type();
+ file->mBlob.swap(blob);
+
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+ }
+ }
+}
+
+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);
+
+ RefPtr<IDBRequest> request = aRequest;
+ RefPtr<IDBTransaction> transaction = aTransaction;
+
+ request->SetError(aErrorCode);
+
+ nsCOMPtr<nsIDOMEvent> errorEvent;
+ if (!aEvent) {
+ // Make an error event and fire it at the target.
+ errorEvent = CreateGenericEvent(request,
+ nsDependentString(kErrorEventType),
+ eDoesBubble,
+ eCancelable);
+ MOZ_ASSERT(errorEvent);
+
+ aEvent = errorEvent;
+ }
+
+ Maybe<AutoSetCurrentTransaction> asct;
+ if (aTransaction) {
+ asct.emplace(aTransaction);
+ }
+
+ if (transaction) {
+ IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "Firing %s event with error 0x%x",
+ "IndexedDB %s: C T[%lld] R[%llu]: %s (0x%x)",
+ IDB_LOG_ID_STRING(),
+ transaction->LoggingSerialNumber(),
+ request->LoggingSerialNumber(),
+ IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
+ aErrorCode);
+ } else {
+ IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: "
+ "Firing %s event with error 0x%x",
+ "IndexedDB %s: C R[%llu]: %s (0x%x)",
+ IDB_LOG_ID_STRING(),
+ request->LoggingSerialNumber(),
+ IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
+ aErrorCode);
+ }
+
+ bool doDefault;
+ nsresult rv = request->DispatchEvent(aEvent, &doDefault);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted());
+
+ // Do not abort the transaction here if this request is failed due to the
+ // abortion of its transaction to ensure that the correct error cause of
+ // the abort event be set in IDBTransaction::FireCompleteOrAbortEvents() later.
+ if (transaction && transaction->IsOpen() &&
+ aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) {
+ WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
+ MOZ_ASSERT(internalEvent);
+
+ if (internalEvent->mFlags.mExceptionWasRaised) {
+ transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ } else if (doDefault) {
+ transaction->Abort(request);
+ }
+ }
+}
+
+void
+DispatchSuccessEvent(ResultHelper* aResultHelper,
+ nsIDOMEvent* aEvent = nullptr)
+{
+ MOZ_ASSERT(aResultHelper);
+
+ PROFILER_LABEL("IndexedDB",
+ "DispatchSuccessEvent",
+ js::ProfileEntry::Category::STORAGE);
+
+ RefPtr<IDBRequest> request = aResultHelper->Request();
+ MOZ_ASSERT(request);
+ request->AssertIsOnOwningThread();
+
+ RefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
+
+ if (transaction && transaction->IsAborted()) {
+ DispatchErrorEvent(request, transaction->AbortCode(), transaction);
+ return;
+ }
+
+ nsCOMPtr<nsIDOMEvent> successEvent;
+ if (!aEvent) {
+ successEvent = CreateGenericEvent(request,
+ nsDependentString(kSuccessEventType),
+ eDoesNotBubble,
+ eNotCancelable);
+ MOZ_ASSERT(successEvent);
+
+ aEvent = successEvent;
+ }
+
+ request->SetResultCallback(aResultHelper);
+
+ MOZ_ASSERT(aEvent);
+ MOZ_ASSERT_IF(transaction, transaction->IsOpen());
+
+ if (transaction) {
+ IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
+ "Firing %s event",
+ "IndexedDB %s: C T[%lld] R[%llu]: %s",
+ IDB_LOG_ID_STRING(),
+ transaction->LoggingSerialNumber(),
+ request->LoggingSerialNumber(),
+ IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
+ } else {
+ IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: Firing %s event",
+ "IndexedDB %s: C R[%llu]: %s",
+ IDB_LOG_ID_STRING(),
+ request->LoggingSerialNumber(),
+ IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
+ }
+
+ 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->WidgetEventPtr();
+ MOZ_ASSERT(internalEvent);
+
+ if (transaction &&
+ transaction->IsOpen() &&
+ internalEvent->mFlags.mExceptionWasRaised) {
+ transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ }
+}
+
+PRFileDesc*
+GetFileDescriptorFromStream(nsIInputStream* aStream)
+{
+ MOZ_ASSERT(aStream);
+
+ nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
+ if (NS_WARN_IF(!fileMetadata)) {
+ return nullptr;
+ }
+
+ PRFileDesc* fileDesc;
+ nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(fileDesc);
+
+ return fileDesc;
+}
+
+class WorkerPermissionChallenge;
+
+// This class calles WorkerPermissionChallenge::OperationCompleted() in the
+// worker thread.
+class WorkerPermissionOperationCompleted final : public WorkerControlRunnable
+{
+ RefPtr<WorkerPermissionChallenge> mChallenge;
+
+public:
+ WorkerPermissionOperationCompleted(WorkerPrivate* aWorkerPrivate,
+ WorkerPermissionChallenge* aChallenge)
+ : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+ , mChallenge(aChallenge)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ virtual bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
+};
+
+// This class used to do prompting in the main thread and main process.
+class WorkerPermissionRequest final : public PermissionRequestBase
+{
+ RefPtr<WorkerPermissionChallenge> mChallenge;
+
+public:
+ WorkerPermissionRequest(Element* aElement,
+ nsIPrincipal* aPrincipal,
+ WorkerPermissionChallenge* aChallenge)
+ : PermissionRequestBase(aElement, aPrincipal)
+ , mChallenge(aChallenge)
+ {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChallenge);
+ }
+
+private:
+ ~WorkerPermissionRequest()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ virtual void
+ OnPromptComplete(PermissionValue aPermissionValue) override;
+};
+
+// This class is used in the main thread of all child processes.
+class WorkerPermissionRequestChildProcessActor final
+ : public PIndexedDBPermissionRequestChild
+{
+ RefPtr<WorkerPermissionChallenge> mChallenge;
+
+public:
+ explicit WorkerPermissionRequestChildProcessActor(
+ WorkerPermissionChallenge* aChallenge)
+ : mChallenge(aChallenge)
+ {
+ MOZ_ASSERT(!XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChallenge);
+ }
+
+protected:
+ ~WorkerPermissionRequestChildProcessActor()
+ {}
+
+ virtual bool
+ Recv__delete__(const uint32_t& aPermission) override;
+};
+
+class WorkerPermissionChallenge final : public Runnable
+{
+public:
+ WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate,
+ BackgroundFactoryRequestChild* aActor,
+ IDBFactory* aFactory,
+ const PrincipalInfo& aPrincipalInfo)
+ : mWorkerPrivate(aWorkerPrivate)
+ , mActor(aActor)
+ , mFactory(aFactory)
+ , mPrincipalInfo(aPrincipalInfo)
+ {
+ MOZ_ASSERT(mWorkerPrivate);
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aFactory);
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ bool
+ Dispatch()
+ {
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ if (NS_WARN_IF(!mWorkerPrivate->ModifyBusyCountFromWorker(true))) {
+ return false;
+ }
+
+ if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
+ mWorkerPrivate->ModifyBusyCountFromWorker(false);
+ return false;
+ }
+
+ return true;
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ bool completed = RunInternal();
+ if (completed) {
+ OperationCompleted();
+ }
+
+ return NS_OK;
+ }
+
+ void
+ OperationCompleted()
+ {
+ if (NS_IsMainThread()) {
+ RefPtr<WorkerPermissionOperationCompleted> runnable =
+ new WorkerPermissionOperationCompleted(mWorkerPrivate, this);
+
+ MOZ_ALWAYS_TRUE(runnable->Dispatch());
+ return;
+ }
+
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ RefPtr<IDBFactory> factory;
+ mFactory.swap(factory);
+
+ mActor->SendPermissionRetry();
+ mActor = nullptr;
+
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ mWorkerPrivate->ModifyBusyCountFromWorker(false);
+ }
+
+private:
+ bool
+ RunInternal()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Walk up to our containing page
+ WorkerPrivate* wp = mWorkerPrivate;
+ while (wp->GetParent()) {
+ wp = wp->GetParent();
+ }
+
+ nsPIDOMWindowInner* window = wp->GetWindow();
+ if (!window) {
+ return true;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> principal =
+ mozilla::ipc::PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
+
+ if (XRE_IsParentProcess()) {
+ nsCOMPtr<Element> ownerElement =
+ do_QueryInterface(window->GetChromeEventHandler());
+ if (NS_WARN_IF(!ownerElement)) {
+ return true;
+ }
+
+ RefPtr<WorkerPermissionRequest> helper =
+ new WorkerPermissionRequest(ownerElement, principal, this);
+
+ PermissionRequestBase::PermissionValue permission;
+ if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) {
+ return true;
+ }
+
+ MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
+ permission == PermissionRequestBase::kPermissionDenied ||
+ permission == PermissionRequestBase::kPermissionPrompt);
+
+ return permission != PermissionRequestBase::kPermissionPrompt;
+ }
+
+ TabChild* tabChild = TabChild::GetFrom(window);
+ MOZ_ASSERT(tabChild);
+
+ IPC::Principal ipcPrincipal(principal);
+
+ auto* actor = new WorkerPermissionRequestChildProcessActor(this);
+ tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal);
+ return false;
+ }
+
+private:
+ WorkerPrivate* mWorkerPrivate;
+ BackgroundFactoryRequestChild* mActor;
+ RefPtr<IDBFactory> mFactory;
+ PrincipalInfo mPrincipalInfo;
+};
+
+void
+WorkerPermissionRequest::OnPromptComplete(PermissionValue aPermissionValue)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mChallenge->OperationCompleted();
+}
+
+bool
+WorkerPermissionOperationCompleted::WorkerRun(JSContext* aCx,
+ WorkerPrivate* aWorkerPrivate)
+{
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ mChallenge->OperationCompleted();
+ return true;
+}
+
+bool
+WorkerPermissionRequestChildProcessActor::Recv__delete__(
+ const uint32_t& /* aPermission */)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mChallenge->OperationCompleted();
+ return true;
+}
+
+} // namespace
+
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+// CancelableRunnable is used to make workers happy.
+class BackgroundRequestChild::PreprocessHelper final
+ : public CancelableRunnable
+{
+ typedef std::pair<nsCOMPtr<nsIInputStream>,
+ nsCOMPtr<nsIInputStream>> StreamPair;
+
+ nsCOMPtr<nsIEventTarget> mOwningThread;
+ nsTArray<StreamPair> mStreamPairs;
+ nsTArray<RefPtr<JS::WasmModule>> mModuleSet;
+ BackgroundRequestChild* mActor;
+ uint32_t mModuleSetIndex;
+ nsresult mResultCode;
+
+public:
+ PreprocessHelper(uint32_t aModuleSetIndex, BackgroundRequestChild* aActor)
+ : mOwningThread(NS_GetCurrentThread())
+ , mActor(aActor)
+ , mModuleSetIndex(aModuleSetIndex)
+ , mResultCode(NS_OK)
+ {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+ aActor->AssertIsOnOwningThread();
+ }
+
+ bool
+ IsOnOwningThread() const
+ {
+ MOZ_ASSERT(mOwningThread);
+
+ bool current;
+ return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)) && current;
+ }
+
+ void
+ AssertIsOnOwningThread() const
+ {
+ MOZ_ASSERT(IsOnOwningThread());
+ }
+
+ void
+ ClearActor()
+ {
+ AssertIsOnOwningThread();
+
+ mActor = nullptr;
+ }
+
+ nsresult
+ Init(const nsTArray<StructuredCloneFile>& aFiles);
+
+ nsresult
+ Dispatch();
+
+private:
+ ~PreprocessHelper()
+ { }
+
+ void
+ RunOnOwningThread();
+
+ nsresult
+ RunOnStreamTransportThread();
+
+ NS_DECL_NSIRUNNABLE
+
+ virtual nsresult
+ Cancel() override;
+};
+
+/*******************************************************************************
+ * 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();
+
+ RefPtr<IDBFactory> factory;
+ mFactory.swap(factory);
+
+ mActor->SendPermissionRetry();
+ mActor = nullptr;
+
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundRequestChildBase
+ ******************************************************************************/
+
+BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest)
+ : mRequest(aRequest)
+{
+ 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
+
+/*******************************************************************************
+ * 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(&current)));
+ MOZ_ASSERT(current);
+}
+
+nsIEventTarget*
+BackgroundFactoryChild::OwningThread() const
+{
+ MOZ_ASSERT(mOwningThread);
+ return mOwningThread;
+}
+
+#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<BackgroundFactoryRequestChild*>(aActor);
+ return true;
+}
+
+PBackgroundIDBDatabaseChild*
+BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild(
+ const DatabaseSpec& aSpec,
+ PBackgroundIDBFactoryRequestChild* aRequest)
+{
+ AssertIsOnOwningThread();
+
+ auto request = static_cast<BackgroundFactoryRequestChild*>(aRequest);
+ MOZ_ASSERT(request);
+
+ return new BackgroundDatabaseChild(aSpec, request);
+}
+
+bool
+BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild(
+ PBackgroundIDBDatabaseChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundDatabaseChild*>(aActor);
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundFactoryRequestChild
+ ******************************************************************************/
+
+BackgroundFactoryRequestChild::BackgroundFactoryRequestChild(
+ IDBFactory* aFactory,
+ IDBOpenDBRequest* aOpenRequest,
+ bool aIsDeleteOp,
+ uint64_t aRequestedVersion)
+ : BackgroundRequestChildBase(aOpenRequest)
+ , mFactory(aFactory)
+ , mRequestedVersion(aRequestedVersion)
+ , 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();
+
+ return static_cast<IDBOpenDBRequest*>(mRequest.get());
+}
+
+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<BackgroundDatabaseChild*>(aResponse.databaseChild());
+ MOZ_ASSERT(databaseActor);
+
+ IDBDatabase* database = databaseActor->GetDOMObject();
+ if (!database) {
+ databaseActor->EnsureDOMObject();
+
+ database = databaseActor->GetDOMObject();
+ MOZ_ASSERT(database);
+
+ MOZ_ASSERT(!database->IsClosed());
+ }
+
+ if (database->IsClosed()) {
+ // If the database was closed already, which is only possible if we fired an
+ // "upgradeneeded" event, then we shouldn't fire a "success" event here.
+ // Instead we fire an error event with AbortErr.
+ DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ } else {
+ ResultHelper helper(mRequest, nullptr, database);
+
+ DispatchSuccessEvent(&helper);
+ }
+
+ databaseActor->ReleaseDOMObject();
+
+ return true;
+}
+
+bool
+BackgroundFactoryRequestChild::HandleResponse(
+ const DeleteDatabaseRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue);
+
+ nsCOMPtr<nsIDOMEvent> successEvent =
+ IDBVersionChangeEvent::Create(mRequest,
+ nsDependentString(kSuccessEventType),
+ aResponse.previousVersion());
+ MOZ_ASSERT(successEvent);
+
+ DispatchSuccessEvent(&helper, successEvent);
+
+ return true;
+}
+
+void
+BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (aWhy != Deletion) {
+ IDBOpenDBRequest* openRequest = GetOpenDBRequest();
+ if (openRequest) {
+ openRequest->NoteComplete();
+ }
+ }
+}
+
+bool
+BackgroundFactoryRequestChild::Recv__delete__(
+ const FactoryRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ bool result;
+
+ switch (aResponse.type()) {
+ case FactoryRequestResponse::Tnsresult:
+ result = HandleResponse(aResponse.get_nsresult());
+ break;
+
+ case FactoryRequestResponse::TOpenDatabaseRequestResponse:
+ result = HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
+ break;
+
+ case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
+ result = HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
+ break;
+
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+
+ IDBOpenDBRequest* request = GetOpenDBRequest();
+ MOZ_ASSERT(request);
+
+ request->NoteComplete();
+
+ if (NS_WARN_IF(!result)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+BackgroundFactoryRequestChild::RecvPermissionChallenge(
+ const PrincipalInfo& aPrincipalInfo)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (!NS_IsMainThread()) {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+ workerPrivate->AssertIsOnWorkerThread();
+
+ RefPtr<WorkerPermissionChallenge> challenge =
+ new WorkerPermissionChallenge(workerPrivate, this, mFactory,
+ aPrincipalInfo);
+ return challenge->Dispatch();
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> principal =
+ mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ if (XRE_IsParentProcess()) {
+ nsCOMPtr<nsPIDOMWindowInner> window = mFactory->GetParentObject();
+ MOZ_ASSERT(window);
+
+ nsCOMPtr<Element> ownerElement =
+ do_QueryInterface(window->GetChromeEventHandler());
+ if (NS_WARN_IF(!ownerElement)) {
+ // If this fails, the page was navigated. Fail the permission check by
+ // forcing an immediate retry.
+ return SendPermissionRetry();
+ }
+
+ RefPtr<PermissionRequestMainProcessHelper> helper =
+ new PermissionRequestMainProcessHelper(this, mFactory, ownerElement, 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;
+ }
+
+ RefPtr<TabChild> 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<nsIDOMEvent> blockedEvent;
+ if (mIsDeleteOp) {
+ blockedEvent =
+ IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion);
+ MOZ_ASSERT(blockedEvent);
+ } else {
+ blockedEvent =
+ IDBVersionChangeEvent::Create(mRequest,
+ type,
+ aCurrentVersion,
+ mRequestedVersion);
+ MOZ_ASSERT(blockedEvent);
+ }
+
+ RefPtr<IDBRequest> kungFuDeathGrip = mRequest;
+
+ IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: Firing \"blocked\" event",
+ "IndexedDB %s: C R[%llu]: \"blocked\"",
+ IDB_LOG_ID_STRING(),
+ kungFuDeathGrip->LoggingSerialNumber());
+
+ bool dummy;
+ if (NS_FAILED(kungFuDeathGrip->DispatchEvent(blockedEvent, &dummy))) {
+ NS_WARNING("Failed to dispatch event!");
+ }
+
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundDatabaseChild
+ ******************************************************************************/
+
+BackgroundDatabaseChild::BackgroundDatabaseChild(
+ const DatabaseSpec& aSpec,
+ BackgroundFactoryRequestChild* aOpenRequestActor)
+ : mSpec(new DatabaseSpec(aSpec))
+ , mOpenRequestActor(aOpenRequestActor)
+ , mDatabase(nullptr)
+{
+ // 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->GetOpenDBRequest();
+ MOZ_ASSERT(request);
+
+ auto factory =
+ static_cast<BackgroundFactoryChild*>(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(
+ PBlobChild* aBlobChild)
+{
+ MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild(
+ PBackgroundIDBDatabaseFileChild* aActor)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+
+ delete aActor;
+ return true;
+}
+
+PBackgroundIDBDatabaseRequestChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseRequestChild(
+ const DatabaseRequestParams& aParams)
+{
+ MOZ_CRASH("PBackgroundIDBDatabaseRequestChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseRequestChild(
+ PBackgroundIDBDatabaseRequestChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundDatabaseRequestChild*>(aActor);
+ return true;
+}
+
+PBackgroundIDBTransactionChild*
+BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild(
+ const nsTArray<nsString>& aObjectStoreNames,
+ const Mode& aMode)
+{
+ MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBTransactionChild(
+ PBackgroundIDBTransactionChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundTransactionChild*>(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<BackgroundVersionChangeTransactionChild*>(aActor);
+
+ RefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
+ MOZ_ASSERT(request);
+
+ RefPtr<IDBTransaction> transaction =
+ IDBTransaction::CreateVersionChange(mDatabase,
+ actor,
+ request,
+ aNextObjectStoreId,
+ aNextIndexId);
+ if (NS_WARN_IF(!transaction)) {
+ // This can happen if we receive events after a worker has begun its
+ // shutdown process.
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ // Report this to the console.
+ IDB_REPORT_INTERNAL_ERR();
+
+ MOZ_ALWAYS_TRUE(aActor->SendDeleteMe());
+ return true;
+ }
+
+ transaction->AssertIsOnOwningThread();
+
+ actor->SetDOMTransaction(transaction);
+
+ mDatabase->EnterSetVersionTransaction(aRequestedVersion);
+
+ request->SetTransaction(transaction);
+
+ nsCOMPtr<nsIDOMEvent> upgradeNeededEvent =
+ IDBVersionChangeEvent::Create(request,
+ nsDependentString(kUpgradeNeededEventType),
+ aCurrentVersion,
+ aRequestedVersion);
+ MOZ_ASSERT(upgradeNeededEvent);
+
+ ResultHelper helper(request, transaction, mDatabase);
+
+ DispatchSuccessEvent(&helper, upgradeNeededEvent);
+
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild(
+ PBackgroundIDBVersionChangeTransactionChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
+ return true;
+}
+
+PBackgroundMutableFileChild*
+BackgroundDatabaseChild::AllocPBackgroundMutableFileChild(const nsString& aName,
+ const nsString& aType)
+{
+ AssertIsOnOwningThread();
+
+#ifdef DEBUG
+ nsCOMPtr<nsIThread> owningThread = do_QueryInterface(OwningThread());
+
+ PRThread* owningPRThread;
+ owningThread->GetPRThread(&owningPRThread);
+#endif
+
+ return new BackgroundMutableFileChild(DEBUGONLY(owningPRThread,)
+ aName,
+ aType);
+}
+
+bool
+BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild(
+ PBackgroundMutableFileChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BackgroundMutableFileChild*>(aActor);
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
+ const NullableVersion& aNewVersion)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (!mDatabase || mDatabase->IsClosed()) {
+ return true;
+ }
+
+ RefPtr<IDBDatabase> kungFuDeathGrip = mDatabase;
+
+ // Handle bfcache'd windows.
+ if (nsPIDOMWindowInner* owner = kungFuDeathGrip->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<nsIDocument> doc = owner->GetExtantDoc()) {
+ if (nsCOMPtr<nsIBFCacheEntry> 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.
+ kungFuDeathGrip->AbortTransactions(/* aShouldWarn */ false);
+ kungFuDeathGrip->Close();
+ return true;
+ }
+ }
+
+ // Otherwise fire a versionchange event.
+ const nsDependentString type(kVersionChangeEventType);
+
+ nsCOMPtr<nsIDOMEvent> versionChangeEvent;
+
+ switch (aNewVersion.type()) {
+ case NullableVersion::Tnull_t:
+ versionChangeEvent =
+ IDBVersionChangeEvent::Create(kungFuDeathGrip, type, aOldVersion);
+ MOZ_ASSERT(versionChangeEvent);
+ break;
+
+ case NullableVersion::Tuint64_t:
+ versionChangeEvent =
+ IDBVersionChangeEvent::Create(kungFuDeathGrip,
+ type,
+ aOldVersion,
+ aNewVersion.get_uint64_t());
+ MOZ_ASSERT(versionChangeEvent);
+ break;
+
+ default:
+ MOZ_CRASH("Should never get here!");
+ }
+
+ IDB_LOG_MARK("IndexedDB %s: Child : Firing \"versionchange\" event",
+ "IndexedDB %s: C: IDBDatabase \"versionchange\" event",
+ IDB_LOG_ID_STRING());
+
+ bool dummy;
+ if (NS_FAILED(kungFuDeathGrip->DispatchEvent(versionChangeEvent, &dummy))) {
+ NS_WARNING("Failed to dispatch event!");
+ }
+
+ if (!kungFuDeathGrip->IsClosed()) {
+ SendBlocked();
+ }
+
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvInvalidate()
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mDatabase) {
+ mDatabase->Invalidate();
+ }
+
+ return true;
+}
+
+bool
+BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete()
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mDatabase) {
+ mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType));
+ }
+
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundDatabaseRequestChild
+ ******************************************************************************/
+
+BackgroundDatabaseRequestChild::BackgroundDatabaseRequestChild(
+ IDBDatabase* aDatabase,
+ IDBRequest* aRequest)
+ : BackgroundRequestChildBase(aRequest)
+ , mDatabase(aDatabase)
+{
+ // Can't assert owning thread here because IPDL has not yet set our manager!
+ MOZ_ASSERT(aDatabase);
+ aDatabase->AssertIsOnOwningThread();
+ MOZ_ASSERT(aRequest);
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseRequestChild);
+}
+
+BackgroundDatabaseRequestChild::~BackgroundDatabaseRequestChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseRequestChild);
+}
+
+bool
+BackgroundDatabaseRequestChild::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
+BackgroundDatabaseRequestChild::HandleResponse(
+ const CreateFileRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ mRequest->Reset();
+
+ auto mutableFileActor =
+ static_cast<BackgroundMutableFileChild*>(aResponse.mutableFileChild());
+ MOZ_ASSERT(mutableFileActor);
+
+ mutableFileActor->EnsureDOMObject();
+
+ auto mutableFile =
+ static_cast<IDBMutableFile*>(mutableFileActor->GetDOMObject());
+ MOZ_ASSERT(mutableFile);
+
+ ResultHelper helper(mRequest, nullptr, mutableFile);
+
+ DispatchSuccessEvent(&helper);
+
+ mutableFileActor->ReleaseDOMObject();
+
+ return true;
+}
+
+bool
+BackgroundDatabaseRequestChild::Recv__delete__(
+ const DatabaseRequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+
+ switch (aResponse.type()) {
+ case DatabaseRequestResponse::Tnsresult:
+ return HandleResponse(aResponse.get_nsresult());
+
+ case DatabaseRequestResponse::TCreateFileRequestResponse:
+ return HandleResponse(aResponse.get_CreateFileRequestResponse());
+
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+/*******************************************************************************
+ * 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<BackgroundDatabaseChild*>(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<BackgroundRequestChild*>(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<BackgroundCursorChild*>(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<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+BackgroundVersionChangeTransactionChild::SendDeleteMeInternal(
+ bool aFailedConstructor)
+{
+ AssertIsOnOwningThread();
+
+ if (mTransaction || aFailedConstructor) {
+ 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<BackgroundRequestChild*>(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<BackgroundCursorChild*>(aActor);
+ return true;
+}
+
+/*******************************************************************************
+ * BackgroundMutableFileChild
+ ******************************************************************************/
+
+BackgroundMutableFileChild::BackgroundMutableFileChild(
+ DEBUGONLY(PRThread* aOwningThread,)
+ const nsAString& aName,
+ const nsAString& aType)
+ : BackgroundMutableFileChildBase(DEBUGONLY(aOwningThread))
+ , mName(aName)
+ , mType(aType)
+{
+ // Can't assert owning thread here because IPDL has not yet set our manager!
+ MOZ_COUNT_CTOR(indexedDB::BackgroundMutableFileChild);
+}
+
+BackgroundMutableFileChild::~BackgroundMutableFileChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundMutableFileChild);
+}
+
+already_AddRefed<MutableFileBase>
+BackgroundMutableFileChild::CreateMutableFile()
+{
+ auto database =
+ static_cast<BackgroundDatabaseChild*>(Manager())->GetDOMObject();
+ MOZ_ASSERT(database);
+
+ RefPtr<IDBMutableFile> mutableFile =
+ new IDBMutableFile(database, this, mName, mType);
+
+ return mutableFile.forget();
+}
+
+/*******************************************************************************
+ * BackgroundRequestChild
+ ******************************************************************************/
+
+BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest)
+ : BackgroundRequestChildBase(aRequest)
+ , mTransaction(aRequest->GetTransaction())
+ , mRunningPreprocessHelpers(0)
+ , mCurrentModuleSetIndex(0)
+ , mPreprocessResultCode(NS_OK)
+ , mGetAll(false)
+{
+ MOZ_ASSERT(mTransaction);
+ mTransaction->AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild);
+}
+
+BackgroundRequestChild::~BackgroundRequestChild()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mTransaction);
+
+ MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
+}
+
+void
+BackgroundRequestChild::MaybeSendContinue()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRunningPreprocessHelpers > 0);
+
+ if (--mRunningPreprocessHelpers == 0) {
+ PreprocessResponse response;
+
+ if (NS_SUCCEEDED(mPreprocessResultCode)) {
+ if (mGetAll) {
+ response = ObjectStoreGetAllPreprocessResponse();
+ } else {
+ response = ObjectStoreGetPreprocessResponse();
+ }
+ } else {
+ response = mPreprocessResultCode;
+ }
+
+ MOZ_ALWAYS_TRUE(SendContinue(response));
+ }
+}
+
+void
+BackgroundRequestChild::OnPreprocessFinished(
+ uint32_t aModuleSetIndex,
+ nsTArray<RefPtr<JS::WasmModule>>& aModuleSet)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aModuleSetIndex < mPreprocessHelpers.Length());
+ MOZ_ASSERT(!aModuleSet.IsEmpty());
+ MOZ_ASSERT(mPreprocessHelpers[aModuleSetIndex]);
+ MOZ_ASSERT(mModuleSets[aModuleSetIndex].IsEmpty());
+
+ mModuleSets[aModuleSetIndex].SwapElements(aModuleSet);
+
+ MaybeSendContinue();
+
+ mPreprocessHelpers[aModuleSetIndex] = nullptr;
+}
+
+void
+BackgroundRequestChild::OnPreprocessFailed(uint32_t aModuleSetIndex,
+ nsresult aErrorCode)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aModuleSetIndex < mPreprocessHelpers.Length());
+ MOZ_ASSERT(NS_FAILED(aErrorCode));
+ MOZ_ASSERT(mPreprocessHelpers[aModuleSetIndex]);
+ MOZ_ASSERT(mModuleSets[aModuleSetIndex].IsEmpty());
+
+ if (NS_SUCCEEDED(mPreprocessResultCode)) {
+ mPreprocessResultCode = aErrorCode;
+ }
+
+ MaybeSendContinue();
+
+ mPreprocessHelpers[aModuleSetIndex] = nullptr;
+}
+
+const nsTArray<RefPtr<JS::WasmModule>>*
+BackgroundRequestChild::GetNextModuleSet(const StructuredCloneReadInfo& aInfo)
+{
+ if (!aInfo.mHasPreprocessInfo) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(mCurrentModuleSetIndex < mModuleSets.Length());
+ MOZ_ASSERT(!mModuleSets[mCurrentModuleSetIndex].IsEmpty());
+ return &mModuleSets[mCurrentModuleSetIndex++];
+}
+
+void
+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);
+}
+
+void
+BackgroundRequestChild::HandleResponse(const Key& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundRequestChild::HandleResponse(
+ const SerializedStructuredCloneReadInfo& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ // XXX Fix this somehow...
+ auto& serializedCloneInfo =
+ const_cast<SerializedStructuredCloneReadInfo&>(aResponse);
+
+ StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo));
+
+ DeserializeStructuredCloneFiles(mTransaction->Database(),
+ aResponse.files(),
+ GetNextModuleSet(cloneReadInfo),
+ cloneReadInfo.mFiles);
+
+ ResultHelper helper(mRequest, mTransaction, &cloneReadInfo);
+
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundRequestChild::HandleResponse(
+ const nsTArray<SerializedStructuredCloneReadInfo>& aResponse)
+{
+ AssertIsOnOwningThread();
+
+ nsTArray<StructuredCloneReadInfo> 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<SerializedStructuredCloneReadInfo&>(aResponse[index]);
+
+ StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement();
+
+ // Move relevant data into the cloneReadInfo
+ *cloneReadInfo = Move(serializedCloneInfo);
+
+ // Get the files
+ nsTArray<StructuredCloneFile> files;
+ DeserializeStructuredCloneFiles(database,
+ serializedCloneInfo.files(),
+ GetNextModuleSet(*cloneReadInfo),
+ files);
+
+ cloneReadInfo->mFiles = Move(files);
+ }
+ }
+
+ ResultHelper helper(mRequest, mTransaction, &cloneReadInfos);
+
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
+{
+ AssertIsOnOwningThread();
+
+ ResultHelper helper(mRequest, mTransaction, &aResponse);
+
+ DispatchSuccessEvent(&helper);
+}
+
+void
+BackgroundRequestChild::HandleResponse(uint64_t aResponse)
+{
+ AssertIsOnOwningThread();
+
+ JS::Value response(JS::NumberValue(aResponse));
+
+ ResultHelper helper(mRequest, mTransaction, &response);
+
+ DispatchSuccessEvent(&helper);
+}
+
+nsresult
+BackgroundRequestChild::HandlePreprocess(
+ const WasmModulePreprocessInfo& aPreprocessInfo)
+{
+ AssertIsOnOwningThread();
+
+ IDBDatabase* database = mTransaction->Database();
+
+ mPreprocessHelpers.SetLength(1);
+
+ nsTArray<StructuredCloneFile> files;
+ DeserializeStructuredCloneFiles(database,
+ aPreprocessInfo.files(),
+ nullptr,
+ files);
+
+
+ RefPtr<PreprocessHelper>& preprocessHelper = mPreprocessHelpers[0];
+ preprocessHelper = new PreprocessHelper(0, this);
+
+ nsresult rv = preprocessHelper->Init(files);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = preprocessHelper->Dispatch();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mRunningPreprocessHelpers++;
+
+ mModuleSets.SetLength(1);
+
+ return NS_OK;
+}
+
+nsresult
+BackgroundRequestChild::HandlePreprocess(
+ const nsTArray<WasmModulePreprocessInfo>& aPreprocessInfos)
+{
+ AssertIsOnOwningThread();
+
+ IDBDatabase* database = mTransaction->Database();
+
+ uint32_t count = aPreprocessInfos.Length();
+
+ mPreprocessHelpers.SetLength(count);
+
+ // TODO: Since we use the stream transport service, this can spawn 25 threads
+ // and has the potential to cause some annoying browser hiccups.
+ // Consider using a single thread or a very small threadpool.
+ for (uint32_t index = 0; index < count; index++) {
+ const WasmModulePreprocessInfo& preprocessInfo = aPreprocessInfos[index];
+
+ nsTArray<StructuredCloneFile> files;
+ DeserializeStructuredCloneFiles(database,
+ preprocessInfo.files(),
+ nullptr,
+ files);
+
+
+ RefPtr<PreprocessHelper>& preprocessHelper = mPreprocessHelpers[index];
+ preprocessHelper = new PreprocessHelper(index, this);
+
+ nsresult rv = preprocessHelper->Init(files);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = preprocessHelper->Dispatch();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mRunningPreprocessHelpers++;
+ }
+
+ mModuleSets.SetLength(count);
+
+ mGetAll = true;
+
+ return NS_OK;
+}
+
+void
+BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ for (uint32_t count = mPreprocessHelpers.Length(), index = 0;
+ index < count;
+ index++) {
+ RefPtr<PreprocessHelper>& preprocessHelper = mPreprocessHelpers[index];
+
+ if (preprocessHelper) {
+ preprocessHelper->ClearActor();
+
+ preprocessHelper = nullptr;
+ }
+ }
+
+ if (mTransaction) {
+ mTransaction->AssertIsOnOwningThread();
+
+ mTransaction->OnRequestFinished(/* aActorDestroyedNormally */
+ aWhy == Deletion);
+#ifdef DEBUG
+ mTransaction = nullptr;
+#endif
+ }
+}
+
+bool
+BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ if (mTransaction->IsAborted()) {
+ // Always fire an "error" event with ABORT_ERR if the transaction was
+ // aborted, even if the request succeeded or failed with another error.
+ HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+ } else {
+ switch (aResponse.type()) {
+ case RequestResponse::Tnsresult:
+ HandleResponse(aResponse.get_nsresult());
+ break;
+
+ case RequestResponse::TObjectStoreAddResponse:
+ HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
+ break;
+
+ case RequestResponse::TObjectStorePutResponse:
+ HandleResponse(aResponse.get_ObjectStorePutResponse().key());
+ break;
+
+ case RequestResponse::TObjectStoreGetResponse:
+ HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo());
+ break;
+
+ case RequestResponse::TObjectStoreGetKeyResponse:
+ HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key());
+ break;
+
+ case RequestResponse::TObjectStoreGetAllResponse:
+ HandleResponse(aResponse.get_ObjectStoreGetAllResponse().cloneInfos());
+ break;
+
+ case RequestResponse::TObjectStoreGetAllKeysResponse:
+ HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys());
+ break;
+
+ case RequestResponse::TObjectStoreDeleteResponse:
+ HandleResponse(JS::UndefinedHandleValue);
+ break;
+
+ case RequestResponse::TObjectStoreClearResponse:
+ HandleResponse(JS::UndefinedHandleValue);
+ break;
+
+ case RequestResponse::TObjectStoreCountResponse:
+ HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
+ break;
+
+ case RequestResponse::TIndexGetResponse:
+ HandleResponse(aResponse.get_IndexGetResponse().cloneInfo());
+ break;
+
+ case RequestResponse::TIndexGetKeyResponse:
+ HandleResponse(aResponse.get_IndexGetKeyResponse().key());
+ break;
+
+ case RequestResponse::TIndexGetAllResponse:
+ HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos());
+ break;
+
+ case RequestResponse::TIndexGetAllKeysResponse:
+ HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
+ break;
+
+ case RequestResponse::TIndexCountResponse:
+ HandleResponse(aResponse.get_IndexCountResponse().count());
+ break;
+
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+ }
+
+ mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true);
+
+ // Null this out so that we don't try to call OnRequestFinished() again in
+ // ActorDestroy.
+ mTransaction = nullptr;
+
+ return true;
+}
+
+bool
+BackgroundRequestChild::RecvPreprocess(const PreprocessParams& aParams)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mTransaction);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ nsresult rv;
+
+ switch (aParams.type()) {
+ case PreprocessParams::TObjectStoreGetPreprocessParams: {
+ ObjectStoreGetPreprocessParams params =
+ aParams.get_ObjectStoreGetPreprocessParams();
+
+ rv = HandlePreprocess(params.preprocessInfo());
+
+ break;
+ }
+
+ case PreprocessParams::TObjectStoreGetAllPreprocessParams: {
+ ObjectStoreGetAllPreprocessParams params =
+ aParams.get_ObjectStoreGetAllPreprocessParams();
+
+ rv = HandlePreprocess(params.preprocessInfos());
+
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Unknown params type!");
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return SendContinue(rv);
+ }
+
+ return true;
+}
+
+nsresult
+BackgroundRequestChild::
+PreprocessHelper::Init(const nsTArray<StructuredCloneFile>& aFiles)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!aFiles.IsEmpty());
+
+ uint32_t count = aFiles.Length();
+
+ // We should receive even number of files.
+ MOZ_ASSERT(count % 2 == 0);
+
+ // Let's process it as pairs.
+ count = count / 2;
+
+ nsTArray<StreamPair> streamPairs;
+ for (uint32_t index = 0; index < count; index++) {
+ uint32_t bytecodeIndex = index * 2;
+ uint32_t compiledIndex = bytecodeIndex + 1;
+
+ const StructuredCloneFile& bytecodeFile = aFiles[bytecodeIndex];
+ const StructuredCloneFile& compiledFile = aFiles[compiledIndex];
+
+ MOZ_ASSERT(bytecodeFile.mType == StructuredCloneFile::eWasmBytecode);
+ MOZ_ASSERT(bytecodeFile.mBlob);
+ MOZ_ASSERT(compiledFile.mType == StructuredCloneFile::eWasmCompiled);
+ MOZ_ASSERT(compiledFile.mBlob);
+
+ ErrorResult errorResult;
+
+ nsCOMPtr<nsIInputStream> bytecodeStream;
+ bytecodeFile.mBlob->GetInternalStream(getter_AddRefs(bytecodeStream),
+ errorResult);
+ if (NS_WARN_IF(errorResult.Failed())) {
+ return errorResult.StealNSResult();
+ }
+
+ nsCOMPtr<nsIInputStream> compiledStream;
+ compiledFile.mBlob->GetInternalStream(getter_AddRefs(compiledStream),
+ errorResult);
+ if (NS_WARN_IF(errorResult.Failed())) {
+ return errorResult.StealNSResult();
+ }
+
+ streamPairs.AppendElement(StreamPair(bytecodeStream, compiledStream));
+ }
+
+ mStreamPairs = Move(streamPairs);
+
+ return NS_OK;
+}
+
+nsresult
+BackgroundRequestChild::
+PreprocessHelper::Dispatch()
+{
+ AssertIsOnOwningThread();
+
+ // The stream transport service is used for asynchronous processing. It has
+ // a threadpool with a high cap of 25 threads. Fortunately, the service can
+ // be used on workers too.
+ nsCOMPtr<nsIEventTarget> target =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ MOZ_ASSERT(target);
+
+ nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+BackgroundRequestChild::
+PreprocessHelper::RunOnOwningThread()
+{
+ AssertIsOnOwningThread();
+
+ if (mActor) {
+ if (NS_SUCCEEDED(mResultCode)) {
+ mActor->OnPreprocessFinished(mModuleSetIndex, mModuleSet);
+
+ MOZ_ASSERT(mModuleSet.IsEmpty());
+ } else {
+ mActor->OnPreprocessFailed(mModuleSetIndex, mResultCode);
+ }
+ }
+}
+
+nsresult
+BackgroundRequestChild::
+PreprocessHelper::RunOnStreamTransportThread()
+{
+ MOZ_ASSERT(!IsOnOwningThread());
+ MOZ_ASSERT(!mStreamPairs.IsEmpty());
+ MOZ_ASSERT(mModuleSet.IsEmpty());
+
+ const uint32_t count = mStreamPairs.Length();
+
+ for (uint32_t index = 0; index < count; index++) {
+ const StreamPair& streamPair = mStreamPairs[index];
+
+ const nsCOMPtr<nsIInputStream>& bytecodeStream = streamPair.first;
+
+ MOZ_ASSERT(bytecodeStream);
+
+ PRFileDesc* bytecodeFileDesc = GetFileDescriptorFromStream(bytecodeStream);
+ if (NS_WARN_IF(!bytecodeFileDesc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const nsCOMPtr<nsIInputStream>& compiledStream = streamPair.second;
+
+ MOZ_ASSERT(compiledStream);
+
+ PRFileDesc* compiledFileDesc = GetFileDescriptorFromStream(compiledStream);
+ if (NS_WARN_IF(!compiledFileDesc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ JS::BuildIdCharVector buildId;
+ bool ok = GetBuildId(&buildId);
+ if (NS_WARN_IF(!ok)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<JS::WasmModule> module = JS::DeserializeWasmModule(bytecodeFileDesc,
+ compiledFileDesc,
+ Move(buildId),
+ nullptr,
+ 0,
+ 0);
+ if (NS_WARN_IF(!module)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mModuleSet.AppendElement(module);
+ }
+
+ mStreamPairs.Clear();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BackgroundRequestChild::
+PreprocessHelper::Run()
+{
+ if (IsOnOwningThread()) {
+ RunOnOwningThread();
+ } else {
+ nsresult rv = RunOnStreamTransportThread();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_ASSERT(mResultCode == NS_OK);
+ mResultCode = rv;
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
+ }
+
+ return NS_OK;
+}
+
+nsresult
+BackgroundRequestChild::
+PreprocessHelper::Cancel()
+{
+ return NS_OK;
+}
+
+/*******************************************************************************
+ * BackgroundCursorChild
+ ******************************************************************************/
+
+// Does not need to be threadsafe since this only runs on one thread, but
+// inheriting from CancelableRunnable is easy.
+class BackgroundCursorChild::DelayedActionRunnable final
+ : public CancelableRunnable
+{
+ using ActionFunc = void (BackgroundCursorChild::*)();
+
+ BackgroundCursorChild* mActor;
+ RefPtr<IDBRequest> mRequest;
+ ActionFunc mActionFunc;
+
+public:
+ explicit
+ DelayedActionRunnable(BackgroundCursorChild* aActor, ActionFunc aActionFunc)
+ : mActor(aActor)
+ , mRequest(aActor->mRequest)
+ , mActionFunc(aActionFunc)
+ {
+ MOZ_ASSERT(aActor);
+ aActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mActionFunc);
+ }
+
+private:
+ ~DelayedActionRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+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.
+ 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<nsIRunnable> deleteRunnable = new DelayedActionRunnable(
+ this, &BackgroundCursorChild::SendDeleteMeInternal);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable));
+ }
+}
+
+void
+BackgroundCursorChild::HandleResponse(
+ const nsTArray<ObjectStoreCursorResponse>& aResponses)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mTransaction);
+ MOZ_ASSERT(mObjectStore);
+ MOZ_ASSERT(!mStrongRequest);
+ MOZ_ASSERT(!mStrongCursor);
+
+ MOZ_ASSERT(aResponses.Length() == 1);
+
+ // XXX Fix this somehow...
+ auto& responses =
+ const_cast<nsTArray<ObjectStoreCursorResponse>&>(aResponses);
+
+ for (ObjectStoreCursorResponse& response : responses) {
+ StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo()));
+ cloneReadInfo.mDatabase = mTransaction->Database();
+
+ DeserializeStructuredCloneFiles(mTransaction->Database(),
+ response.cloneInfo().files(),
+ nullptr,
+ cloneReadInfo.mFiles);
+
+ RefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()), Move(cloneReadInfo));
+ } else {
+ newCursor = IDBCursor::Create(this,
+ 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<ObjectStoreKeyCursorResponse&>(aResponse);
+
+ RefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()));
+ } else {
+ newCursor = IDBCursor::Create(this, 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<IndexCursorResponse&>(aResponse);
+
+ StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo()));
+ cloneReadInfo.mDatabase = mTransaction->Database();
+
+ DeserializeStructuredCloneFiles(mTransaction->Database(),
+ aResponse.cloneInfo().files(),
+ nullptr,
+ cloneReadInfo.mFiles);
+
+ RefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()),
+ Move(response.sortKey()),
+ Move(response.objectKey()),
+ Move(cloneReadInfo));
+ } else {
+ newCursor = IDBCursor::Create(this,
+ Move(response.key()),
+ Move(response.sortKey()),
+ 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<IndexKeyCursorResponse&>(aResponse);
+
+ RefPtr<IDBCursor> newCursor;
+
+ if (mCursor) {
+ mCursor->Reset(Move(response.key()),
+ Move(response.sortKey()),
+ Move(response.objectKey()));
+ } else {
+ newCursor = IDBCursor::Create(this,
+ Move(response.key()),
+ Move(response.sortKey()),
+ 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(/* aActorDestroyedNormally */
+ aWhy == Deletion);
+ }
+
+ 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_IF(mCursor, mStrongCursor);
+ MOZ_ASSERT_IF(!mCursor, mStrongRequest);
+
+ MaybeCollectGarbageOnIPCMessage();
+
+ RefPtr<IDBRequest> request;
+ mStrongRequest.swap(request);
+
+ RefPtr<IDBCursor> 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::TArrayOfObjectStoreCursorResponse:
+ HandleResponse(aResponse.get_ArrayOfObjectStoreCursorResponse());
+ 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(/* aActorDestroyedNormally */ true);
+
+ return true;
+}
+
+NS_IMETHODIMP
+BackgroundCursorChild::
+DelayedActionRunnable::Run()
+{
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(mRequest);
+ MOZ_ASSERT(mActionFunc);
+
+ (mActor->*mActionFunc)();
+
+ mActor = nullptr;
+ mRequest = nullptr;
+
+ return NS_OK;
+}
+
+nsresult
+BackgroundCursorChild::
+DelayedActionRunnable::Cancel()
+{
+ if (NS_WARN_IF(!mActor)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // This must always run to clean up our state.
+ Run();
+
+ return NS_OK;
+}
+
+/*******************************************************************************
+ * BackgroundUtilsChild
+ ******************************************************************************/
+
+BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager)
+ : mManager(aManager)
+#ifdef DEBUG
+ , mOwningThread(NS_GetCurrentThread())
+#endif
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aManager);
+
+ MOZ_COUNT_CTOR(indexedDB::BackgroundUtilsChild);
+}
+
+BackgroundUtilsChild::~BackgroundUtilsChild()
+{
+ MOZ_COUNT_DTOR(indexedDB::BackgroundUtilsChild);
+}
+
+#ifdef DEBUG
+
+void
+BackgroundUtilsChild::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(mOwningThread);
+
+ bool current;
+ MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+ MOZ_ASSERT(current);
+}
+
+#endif // DEBUG
+
+void
+BackgroundUtilsChild::SendDeleteMeInternal()
+{
+ AssertIsOnOwningThread();
+
+ if (mManager) {
+ mManager->ClearBackgroundActor();
+ mManager = nullptr;
+
+ MOZ_ALWAYS_TRUE(PBackgroundIndexedDBUtilsChild::SendDeleteMe());
+ }
+}
+
+void
+BackgroundUtilsChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ if (mManager) {
+ mManager->ClearBackgroundActor();
+#ifdef DEBUG
+ mManager = nullptr;
+#endif
+ }
+}
+
+} // namespace indexedDB
+} // namespace dom
+} // namespace mozilla