/* -*- 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 "FileSnapshot.h" #include "IDBDatabase.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IDBFileHandle.h" #include "IDBIndex.h" #include "IDBMutableFile.h" #include "IDBObjectStore.h" #include "IDBRequest.h" #include "IDBTransaction.h" #include "IndexedDatabase.h" #include "IndexedDatabaseInlines.h" #include "mozilla/BasicEvents.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/Maybe.h" #include "mozilla/TypeTraits.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.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/PendingIPCBlobChild.h" #include "mozilla/dom/IPCBlobUtils.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRunnable.h" #include "mozilla/Encoding.h" #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/TaskQueue.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsIAsyncInputStream.h" #include "nsIBFCacheEntry.h" #include "nsIDocument.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" #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 { namespace indexedDB { /******************************************************************************* * ThreadLocal ******************************************************************************/ ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId) : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1) , mCurrentTransaction(0) { 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(mLoggingIdString.BeginWriting())); } ThreadLocal::~ThreadLocal() { MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal); } /******************************************************************************* * 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* mStructuredCloneArray; const Key* mKey; const nsTArray* mKeyArray; const JS::Value* mJSVal; const JS::Handle* 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* 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) 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 typename EnableIf::value || IsSame::value || IsSame::value, nsresult>::Type GetResult(JSContext* aCx, T* aDOMObject, JS::MutableHandle 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 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* 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_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 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_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 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 mFactory; public: PermissionRequestChildProcessActor(BackgroundFactoryRequestChild* aActor, IDBFactory* aFactory) : mActor(aActor) , mFactory(aFactory) { MOZ_ASSERT(aActor); MOZ_ASSERT(aFactory); aActor->AssertIsOnOwningThread(); } protected: ~PermissionRequestChildProcessActor() { } virtual mozilla::ipc::IPCResult Recv__delete__(const uint32_t& aPermission) override; }; void DeserializeStructuredCloneFiles( IDBDatabase* aDatabase, const nsTArray& aSerializedFiles, const nsTArray>* aModuleSet, nsTArray& 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::TIPCBlob); const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob(); RefPtr blobImpl = IPCBlobUtils::Deserialize(ipcBlob); MOZ_ASSERT(blobImpl); RefPtr blob = Blob::Create(aDatabase->GetOwner(), blobImpl); 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( blobOrMutableFile.get_PBackgroundMutableFileChild()); MOZ_ASSERT(actor); actor->EnsureDOMObject(); auto* mutableFile = static_cast(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::TIPCBlob); const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob(); RefPtr blobImpl = IPCBlobUtils::Deserialize(ipcBlob); MOZ_ASSERT(blobImpl); RefPtr blob = Blob::Create(aDatabase->GetOwner(), blobImpl); 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, Event* aEvent = nullptr) { MOZ_ASSERT(aRequest); aRequest->AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(aErrorCode)); MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); AUTO_PROFILER_LABEL("IndexedDB:DispatchErrorEvent", STORAGE); RefPtr request = aRequest; RefPtr transaction = aTransaction; request->SetError(aErrorCode); RefPtr 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 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); } IgnoredErrorResult rv; bool doDefault = request->DispatchEvent(*aEvent, CallerType::System, rv); if (NS_WARN_IF(rv.Failed())) { 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, Event* aEvent = nullptr) { MOZ_ASSERT(aResultHelper); AUTO_PROFILER_LABEL("IndexedDB:DispatchSuccessEvent", STORAGE); RefPtr request = aResultHelper->Request(); MOZ_ASSERT(request); request->AssertIsOnOwningThread(); RefPtr transaction = aResultHelper->Transaction(); if (transaction && transaction->IsAborted()) { DispatchErrorEvent(request, transaction->AbortCode(), transaction); return; } RefPtr 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)); } MOZ_ASSERT_IF(transaction, transaction->IsOpen() && !transaction->IsAborted()); IgnoredErrorResult rv; request->DispatchEvent(*aEvent, rv); if (NS_WARN_IF(rv.Failed())) { return; } WidgetEvent* internalEvent = aEvent->WidgetEventPtr(); MOZ_ASSERT(internalEvent); if (transaction && transaction->IsOpen()) { if (internalEvent->mFlags.mExceptionWasRaised) { transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); } else { // To handle upgrade transaction. transaction->Run(); } } } PRFileDesc* GetFileDescriptorFromStream(nsIInputStream* aStream) { MOZ_ASSERT(aStream); nsCOMPtr 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 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 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 mChallenge; public: explicit WorkerPermissionRequestChildProcessActor( WorkerPermissionChallenge* aChallenge) : mChallenge(aChallenge) { MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aChallenge); } protected: ~WorkerPermissionRequestChildProcessActor() {} virtual mozilla::ipc::IPCResult Recv__delete__(const uint32_t& aPermission) override; }; class WorkerPermissionChallenge final : public Runnable { public: WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate, BackgroundFactoryRequestChild* aActor, IDBFactory* aFactory, const PrincipalInfo& aPrincipalInfo) : Runnable("indexedDB::WorkerPermissionChallenge") , 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(mWorkerPrivate->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 runnable = new WorkerPermissionOperationCompleted(mWorkerPrivate, this); MOZ_ALWAYS_TRUE(runnable->Dispatch()); return; } MOZ_ASSERT(mActor); mActor->AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); RefPtr 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 principal = mozilla::ipc::PrincipalInfoToPrincipal(mPrincipalInfo, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return true; } if (XRE_IsParentProcess()) { nsCOMPtr ownerElement = do_QueryInterface(window->GetChromeEventHandler()); if (NS_WARN_IF(!ownerElement)) { return true; } RefPtr 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->SetEventTargetForActor(actor, wp->MainThreadEventTarget()); MOZ_ASSERT(actor->GetActorEventTarget()); tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal); return false; } private: WorkerPrivate* mWorkerPrivate; BackgroundFactoryRequestChild* mActor; RefPtr 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; } mozilla::ipc::IPCResult WorkerPermissionRequestChildProcessActor::Recv__delete__( const uint32_t& /* aPermission */) { MOZ_ASSERT(NS_IsMainThread()); mChallenge->OperationCompleted(); return IPC_OK(); } class MOZ_STACK_CLASS AutoSetCurrentFileHandle final { typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl; IDBFileHandle* const mFileHandle; IDBFileHandle* mPreviousFileHandle; IDBFileHandle** mThreadLocalSlot; public: explicit AutoSetCurrentFileHandle(IDBFileHandle* aFileHandle) : mFileHandle(aFileHandle) , mPreviousFileHandle(nullptr) , mThreadLocalSlot(nullptr) { if (aFileHandle) { BackgroundChildImpl::ThreadLocal* threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread(); MOZ_ASSERT(threadLocal); // Hang onto this location for resetting later. mThreadLocalSlot = &threadLocal->mCurrentFileHandle; // Save the current value. mPreviousFileHandle = *mThreadLocalSlot; // Set the new value. *mThreadLocalSlot = aFileHandle; } } ~AutoSetCurrentFileHandle() { MOZ_ASSERT_IF(mThreadLocalSlot, mFileHandle); MOZ_ASSERT_IF(mThreadLocalSlot, *mThreadLocalSlot == mFileHandle); if (mThreadLocalSlot) { // Reset old value. *mThreadLocalSlot = mPreviousFileHandle; } } IDBFileHandle* FileHandle() const { return mFileHandle; } }; class MOZ_STACK_CLASS FileHandleResultHelper final : public IDBFileRequest::ResultCallback { IDBFileRequest* mFileRequest; AutoSetCurrentFileHandle mAutoFileHandle; union { File* mFile; const nsCString* mString; const FileRequestMetadata* mMetadata; const JS::Handle* mJSValHandle; } mResult; enum { ResultTypeFile, ResultTypeString, ResultTypeMetadata, ResultTypeJSValHandle, } mResultType; public: FileHandleResultHelper(IDBFileRequest* aFileRequest, IDBFileHandle* aFileHandle, File* aResult) : mFileRequest(aFileRequest) , mAutoFileHandle(aFileHandle) , mResultType(ResultTypeFile) { MOZ_ASSERT(aFileRequest); MOZ_ASSERT(aFileHandle); MOZ_ASSERT(aResult); mResult.mFile = aResult; } FileHandleResultHelper(IDBFileRequest* aFileRequest, IDBFileHandle* aFileHandle, const nsCString* aResult) : mFileRequest(aFileRequest) , mAutoFileHandle(aFileHandle) , mResultType(ResultTypeString) { MOZ_ASSERT(aFileRequest); MOZ_ASSERT(aFileHandle); MOZ_ASSERT(aResult); mResult.mString = aResult; } FileHandleResultHelper(IDBFileRequest* aFileRequest, IDBFileHandle* aFileHandle, const FileRequestMetadata* aResult) : mFileRequest(aFileRequest) , mAutoFileHandle(aFileHandle) , mResultType(ResultTypeMetadata) { MOZ_ASSERT(aFileRequest); MOZ_ASSERT(aFileHandle); MOZ_ASSERT(aResult); mResult.mMetadata = aResult; } FileHandleResultHelper(IDBFileRequest* aFileRequest, IDBFileHandle* aFileHandle, const JS::Handle* aResult) : mFileRequest(aFileRequest) , mAutoFileHandle(aFileHandle) , mResultType(ResultTypeJSValHandle) { MOZ_ASSERT(aFileRequest); MOZ_ASSERT(aFileHandle); MOZ_ASSERT(aResult); mResult.mJSValHandle = aResult; } IDBFileRequest* FileRequest() const { return mFileRequest; } IDBFileHandle* FileHandle() const { return mAutoFileHandle.FileHandle(); } virtual nsresult GetResult(JSContext* aCx, JS::MutableHandle aResult) override { MOZ_ASSERT(aCx); MOZ_ASSERT(mFileRequest); switch (mResultType) { case ResultTypeFile: return GetResult(aCx, mResult.mFile, aResult); case ResultTypeString: return GetResult(aCx, mResult.mString, aResult); case ResultTypeMetadata: return GetResult(aCx, mResult.mMetadata, aResult); 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, File* aFile, JS::MutableHandle aResult) { bool ok = GetOrCreateDOMReflector(aCx, aFile, aResult); if (NS_WARN_IF(!ok)) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } return NS_OK; } nsresult GetResult(JSContext* aCx, const nsCString* aString, JS::MutableHandle aResult) { const nsCString& data = *aString; nsresult rv; if (!mFileRequest->HasEncoding()) { JS::Rooted arrayBuffer(aCx); rv = nsContentUtils::CreateArrayBuffer(aCx, data, arrayBuffer.address()); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } aResult.setObject(*arrayBuffer); return NS_OK; } // Try the API argument. const Encoding* encoding = Encoding::ForLabel(mFileRequest->GetEncoding()); if (!encoding) { // API argument failed. Since we are dealing with a file system file, // we don't have a meaningful type attribute for the blob available, // so proceeding to the next step, which is defaulting to UTF-8. encoding = UTF_8_ENCODING; } nsString tmpString; Tie(rv, encoding) = encoding->Decode(data, tmpString); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } rv = NS_OK; if (NS_WARN_IF(!xpc::StringToJsval(aCx, tmpString, aResult))) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } return NS_OK; } nsresult GetResult(JSContext* aCx, const FileRequestMetadata* aMetadata, JS::MutableHandle aResult) { JS::Rooted obj(aCx, JS_NewPlainObject(aCx)); if (NS_WARN_IF(!obj)) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } const FileRequestSize& size = aMetadata->size(); if (size.type() != FileRequestSize::Tvoid_t) { MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t); JS::Rooted number(aCx, JS_NumberValue(size.get_uint64_t())); if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "size", number, 0))) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } } const FileRequestLastModified& lastModified = aMetadata->lastModified(); if (lastModified.type() != FileRequestLastModified::Tvoid_t) { MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t); JS::Rooted date(aCx, JS::NewDateObject(aCx, JS::TimeClip(lastModified.get_int64_t()))); if (NS_WARN_IF(!date)) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModified", date, 0))) { return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; } } aResult.setObject(*obj); return NS_OK; } }; already_AddRefed ConvertActorToFile(IDBFileHandle* aFileHandle, const FileRequestGetFileResponse& aResponse) { auto* actor = static_cast(aResponse.fileChild()); IDBMutableFile* mutableFile = aFileHandle->GetMutableFile(); MOZ_ASSERT(mutableFile); const FileRequestMetadata& metadata = aResponse.metadata(); const FileRequestSize& size = metadata.size(); MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t); const FileRequestLastModified& lastModified = metadata.lastModified(); MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t); RefPtr blobImpl = actor->SetPendingInfoAndDeleteActor(mutableFile->Name(), mutableFile->Type(), size.get_uint64_t(), lastModified.get_int64_t()); MOZ_ASSERT(blobImpl); RefPtr blobImplSnapshot = new BlobImplSnapshot(blobImpl, static_cast(aFileHandle)); RefPtr file = File::Create(mutableFile->GetOwner(), blobImplSnapshot); return file.forget(); } void DispatchFileHandleErrorEvent(IDBFileRequest* aFileRequest, nsresult aErrorCode, IDBFileHandle* aFileHandle) { MOZ_ASSERT(aFileRequest); aFileRequest->AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(aErrorCode)); MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_FILEHANDLE); MOZ_ASSERT(aFileHandle); RefPtr fileRequest = aFileRequest; RefPtr fileHandle = aFileHandle; AutoSetCurrentFileHandle ascfh(aFileHandle); fileRequest->FireError(aErrorCode); MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted()); } void DispatchFileHandleSuccessEvent(FileHandleResultHelper* aResultHelper) { MOZ_ASSERT(aResultHelper); RefPtr fileRequest = aResultHelper->FileRequest(); MOZ_ASSERT(fileRequest); fileRequest->AssertIsOnOwningThread(); RefPtr fileHandle = aResultHelper->FileHandle(); MOZ_ASSERT(fileHandle); if (fileHandle->IsAborted()) { fileRequest->FireError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR); return; } MOZ_ASSERT(fileHandle->IsOpen()); fileRequest->SetResultCallback(aResultHelper); MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted()); } } // namespace /******************************************************************************* * Actor class declarations ******************************************************************************/ // CancelableRunnable is used to make workers happy. class BackgroundRequestChild::PreprocessHelper final : public CancelableRunnable , public nsIInputStreamCallback , public nsIFileMetadataCallback { typedef std::pair, nsCOMPtr> StreamPair; nsCOMPtr mOwningEventTarget; nsTArray mStreamPairs; nsTArray> mModuleSet; BackgroundRequestChild* mActor; // These 2 are populated when the processing of the stream pairs runs. PRFileDesc* mCurrentBytecodeFileDesc; PRFileDesc* mCurrentCompiledFileDesc; RefPtr mTaskQueue; nsCOMPtr mTaskQueueEventTarget; uint32_t mModuleSetIndex; nsresult mResultCode; public: PreprocessHelper(uint32_t aModuleSetIndex, BackgroundRequestChild* aActor) : CancelableRunnable("indexedDB::BackgroundRequestChild::PreprocessHelper") , mOwningEventTarget(aActor->GetActorEventTarget()) , mActor(aActor) , mCurrentBytecodeFileDesc(nullptr) , mCurrentCompiledFileDesc(nullptr) , mModuleSetIndex(aModuleSetIndex) , mResultCode(NS_OK) { AssertIsOnOwningThread(); MOZ_ASSERT(aActor); aActor->AssertIsOnOwningThread(); } bool IsOnOwningThread() const { MOZ_ASSERT(mOwningEventTarget); bool current; return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && current; } void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); } void ClearActor() { AssertIsOnOwningThread(); mActor = nullptr; } nsresult Init(const nsTArray& aFiles); nsresult Dispatch(); private: ~PreprocessHelper() { if (mTaskQueue) { mTaskQueue->BeginShutdown(); } } void RunOnOwningThread(); void ProcessCurrentStreamPair(); nsresult WaitForStreamReady(nsIInputStream* aInputStream); void ContinueWithStatus(nsresult aStatus); nsresult DataIsReady(nsIInputStream* aInputStream); NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIRUNNABLE NS_DECL_NSIINPUTSTREAMCALLBACK NS_DECL_NSIFILEMETADATACALLBACK virtual nsresult Cancel() override; }; /******************************************************************************* * Local class implementations ******************************************************************************/ void PermissionRequestMainProcessHelper::OnPromptComplete( PermissionValue aPermissionValue) { MOZ_ASSERT(mActor); mActor->AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); mActor->SendPermissionRetry(); mActor = nullptr; mFactory = nullptr; } mozilla::ipc::IPCResult PermissionRequestChildProcessActor::Recv__delete__( const uint32_t& /* aPermission */) { MOZ_ASSERT(mActor); mActor->AssertIsOnOwningThread(); MOZ_ASSERT(mFactory); MaybeCollectGarbageOnIPCMessage(); RefPtr factory; mFactory.swap(factory); mActor->SendPermissionRetry(); mActor = nullptr; return IPC_OK(); } /******************************************************************************* * 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) { AssertIsOnOwningThread(); MOZ_ASSERT(aFactory); aFactory->AssertIsOnOwningThread(); MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild); } BackgroundFactoryChild::~BackgroundFactoryChild() { MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild); } 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; } mozilla::ipc::IPCResult BackgroundFactoryChild::RecvPBackgroundIDBDatabaseConstructor( PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec, PBackgroundIDBFactoryRequestChild* aRequest) { AssertIsOnOwningThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(aActor->GetActorEventTarget(), "The event target shall be inherited from its manager actor."); return IPC_OK(); } /******************************************************************************* * BackgroundFactoryRequestChild ******************************************************************************/ BackgroundFactoryRequestChild::BackgroundFactoryRequestChild( IDBFactory* aFactory, IDBOpenDBRequest* aOpenRequest, bool aIsDeleteOp, uint64_t aRequestedVersion) : BackgroundRequestChildBase(aOpenRequest) , mFactory(aFactory) , mDatabaseActor(nullptr) , 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(mRequest.get()); } void BackgroundFactoryRequestChild::SetDatabaseActor(BackgroundDatabaseChild* aActor) { AssertIsOnOwningThread(); MOZ_ASSERT(!aActor || !mDatabaseActor); mDatabaseActor = aActor; } 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); if (mDatabaseActor) { mDatabaseActor->ReleaseDOMObject(); MOZ_ASSERT(!mDatabaseActor); } return true; } bool BackgroundFactoryRequestChild::HandleResponse( const OpenDatabaseRequestResponse& aResponse) { AssertIsOnOwningThread(); mRequest->Reset(); auto databaseActor = static_cast(aResponse.databaseChild()); MOZ_ASSERT(databaseActor); IDBDatabase* database = databaseActor->GetDOMObject(); if (!database) { databaseActor->EnsureDOMObject(); MOZ_ASSERT(mDatabaseActor); database = databaseActor->GetDOMObject(); MOZ_ASSERT(database); MOZ_ASSERT(!database->IsClosed()); } MOZ_ASSERT(mDatabaseActor == databaseActor); 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(); MOZ_ASSERT(!mDatabaseActor); return true; } bool BackgroundFactoryRequestChild::HandleResponse( const DeleteDatabaseRequestResponse& aResponse) { AssertIsOnOwningThread(); ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue); RefPtr successEvent = IDBVersionChangeEvent::Create(mRequest, nsDependentString(kSuccessEventType), aResponse.previousVersion()); MOZ_ASSERT(successEvent); DispatchSuccessEvent(&helper, successEvent); MOZ_ASSERT(!mDatabaseActor); return true; } void BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); if (aWhy != Deletion) { IDBOpenDBRequest* openRequest = GetOpenDBRequest(); if (openRequest) { openRequest->NoteComplete(); } } } mozilla::ipc::IPCResult 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 IPC_FAIL_NO_REASON(this); } return IPC_OK(); } mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvPermissionChallenge( const PrincipalInfo& aPrincipalInfo) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); if (!NS_IsMainThread()) { WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr challenge = new WorkerPermissionChallenge(workerPrivate, this, mFactory, aPrincipalInfo); if (!challenge->Dispatch()) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); } nsresult rv; nsCOMPtr principal = mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return IPC_FAIL_NO_REASON(this); } if (XRE_IsParentProcess()) { nsCOMPtr window = mFactory->GetParentObject(); MOZ_ASSERT(window); nsCOMPtr 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. if (!SendPermissionRetry()) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); } RefPtr helper = new PermissionRequestMainProcessHelper(this, mFactory, ownerElement, principal); PermissionRequestBase::PermissionValue permission; if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) { return IPC_FAIL_NO_REASON(this); } MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || permission == PermissionRequestBase::kPermissionDenied || permission == PermissionRequestBase::kPermissionPrompt); if (permission != PermissionRequestBase::kPermissionPrompt) { SendPermissionRetry(); } return IPC_OK(); } RefPtr tabChild = mFactory->GetTabChild(); MOZ_ASSERT(tabChild); IPC::Principal ipcPrincipal(principal); auto* actor = new PermissionRequestChildProcessActor(this, mFactory); tabChild->SetEventTargetForActor(actor, this->GetActorEventTarget()); MOZ_ASSERT(actor->GetActorEventTarget()); tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal); return IPC_OK(); } mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvBlocked(const uint64_t& aCurrentVersion) { AssertIsOnOwningThread(); MOZ_ASSERT(mRequest); MaybeCollectGarbageOnIPCMessage(); const nsDependentString type(kBlockedEventType); RefPtr blockedEvent; if (mIsDeleteOp) { blockedEvent = IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion); MOZ_ASSERT(blockedEvent); } else { blockedEvent = IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion, mRequestedVersion); MOZ_ASSERT(blockedEvent); } RefPtr 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()); IgnoredErrorResult rv; kungFuDeathGrip->DispatchEvent(*blockedEvent, rv); if (rv.Failed()) { NS_WARNING("Failed to dispatch event!"); } return IPC_OK(); } /******************************************************************************* * 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); } #ifdef DEBUG void BackgroundDatabaseChild::AssertIsOnOwningThread() const { static_cast(Manager())->AssertIsOnOwningThread(); } #endif // DEBUG 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(Manager())->GetDOMObject(); MOZ_ASSERT(factory); mTemporaryStrongDatabase = IDBDatabase::Create(request, factory, this, mSpec); MOZ_ASSERT(mTemporaryStrongDatabase); mTemporaryStrongDatabase->AssertIsOnOwningThread(); mDatabase = mTemporaryStrongDatabase; mSpec.forget(); mOpenRequestActor->SetDatabaseActor(this); } void BackgroundDatabaseChild::ReleaseDOMObject() { AssertIsOnOwningThread(); MOZ_ASSERT(mTemporaryStrongDatabase); mTemporaryStrongDatabase->AssertIsOnOwningThread(); MOZ_ASSERT(mOpenRequestActor); MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); mOpenRequestActor->SetDatabaseActor(nullptr); 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 IPCBlob& aIPCBlob) { 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(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); } mozilla::ipc::IPCResult 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(aActor->GetActorEventTarget(), "The event target shall be inherited from its manager actor."); MOZ_ASSERT(mOpenRequestActor); MaybeCollectGarbageOnIPCMessage(); EnsureDOMObject(); auto* actor = static_cast(aActor); RefPtr request = mOpenRequestActor->GetOpenDBRequest(); MOZ_ASSERT(request); RefPtr transaction = IDBTransaction::CreateVersionChange(mDatabase, actor, request, aNextObjectStoreId, aNextIndexId); MOZ_ASSERT(transaction); transaction->AssertIsOnOwningThread(); actor->SetDOMTransaction(transaction); mDatabase->EnterSetVersionTransaction(aRequestedVersion); request->SetTransaction(transaction); RefPtr upgradeNeededEvent = IDBVersionChangeEvent::Create(request, nsDependentString(kUpgradeNeededEventType), aCurrentVersion, aRequestedVersion); MOZ_ASSERT(upgradeNeededEvent); ResultHelper helper(request, transaction, mDatabase); DispatchSuccessEvent(&helper, upgradeNeededEvent); return IPC_OK(); } bool BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild( PBackgroundIDBVersionChangeTransactionChild* aActor) { MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } PBackgroundMutableFileChild* BackgroundDatabaseChild::AllocPBackgroundMutableFileChild(const nsString& aName, const nsString& aType) { AssertIsOnOwningThread(); return new BackgroundMutableFileChild(aName, aType); } bool BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild( PBackgroundMutableFileChild* aActor) { MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, const NullableVersion& aNewVersion) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); if (!mDatabase || mDatabase->IsClosed()) { return IPC_OK(); } RefPtr 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 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. kungFuDeathGrip->AbortTransactions(/* aShouldWarn */ false); kungFuDeathGrip->Close(); return IPC_OK(); } } // Otherwise fire a versionchange event. const nsDependentString type(kVersionChangeEventType); RefPtr 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()); IgnoredErrorResult rv; kungFuDeathGrip->DispatchEvent(*versionChangeEvent, rv); if (rv.Failed()) { NS_WARNING("Failed to dispatch event!"); } if (!kungFuDeathGrip->IsClosed()) { SendBlocked(); } return IPC_OK(); } mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvInvalidate() { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); if (mDatabase) { mDatabase->Invalidate(); } return IPC_OK(); } mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete() { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); if (mDatabase) { mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType)); } return IPC_OK(); } /******************************************************************************* * 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(aResponse.mutableFileChild()); MOZ_ASSERT(mutableFileActor); mutableFileActor->EnsureDOMObject(); auto mutableFile = static_cast(mutableFileActor->GetDOMObject()); MOZ_ASSERT(mutableFile); ResultHelper helper(mRequest, nullptr, mutableFile); DispatchSuccessEvent(&helper); mutableFileActor->ReleaseDOMObject(); return true; } mozilla::ipc::IPCResult BackgroundDatabaseRequestChild::Recv__delete__( const DatabaseRequestResponse& aResponse) { AssertIsOnOwningThread(); MOZ_ASSERT(mRequest); switch (aResponse.type()) { case DatabaseRequestResponse::Tnsresult: if (!HandleResponse(aResponse.get_nsresult())) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); case DatabaseRequestResponse::TCreateFileRequestResponse: if (!HandleResponse(aResponse.get_CreateFileRequestResponse())) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); 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(Manager())->AssertIsOnOwningThread(); } #endif // DEBUG void BackgroundTransactionChild::SendDeleteMeInternal() { AssertIsOnOwningThread(); if (mTransaction) { NoteActorDestroyed(); MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe()); } } void BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); NoteActorDestroyed(); } mozilla::ipc::IPCResult BackgroundTransactionChild::RecvComplete(const nsresult& aResult) { AssertIsOnOwningThread(); MOZ_ASSERT(mTransaction); MaybeCollectGarbageOnIPCMessage(); mTransaction->FireCompleteOrAbortEvents(aResult); NoteComplete(); return IPC_OK(); } 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( bool aFailedConstructor) { AssertIsOnOwningThread(); if (mTransaction || aFailedConstructor) { NoteActorDestroyed(); MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild:: SendDeleteMe()); } } void BackgroundVersionChangeTransactionChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); mOpenDBRequest = nullptr; NoteActorDestroyed(); } mozilla::ipc::IPCResult BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); if (!mTransaction) { return IPC_OK(); } 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 IPC_OK(); } 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; } /******************************************************************************* * BackgroundMutableFileChild ******************************************************************************/ BackgroundMutableFileChild::BackgroundMutableFileChild(const nsAString& aName, const nsAString& aType) : mMutableFile(nullptr) , 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); } #ifdef DEBUG void BackgroundMutableFileChild::AssertIsOnOwningThread() const { static_cast(Manager())->AssertIsOnOwningThread(); } #endif // DEBUG void BackgroundMutableFileChild::EnsureDOMObject() { AssertIsOnOwningThread(); if (mTemporaryStrongMutableFile) { return; } auto database = static_cast(Manager())->GetDOMObject(); MOZ_ASSERT(database); mTemporaryStrongMutableFile = new IDBMutableFile(database, this, mName, mType); MOZ_ASSERT(mTemporaryStrongMutableFile); mTemporaryStrongMutableFile->AssertIsOnOwningThread(); mMutableFile = mTemporaryStrongMutableFile; } void BackgroundMutableFileChild::ReleaseDOMObject() { AssertIsOnOwningThread(); MOZ_ASSERT(mTemporaryStrongMutableFile); mTemporaryStrongMutableFile->AssertIsOnOwningThread(); MOZ_ASSERT(mMutableFile == mTemporaryStrongMutableFile); mTemporaryStrongMutableFile = nullptr; } void BackgroundMutableFileChild::SendDeleteMeInternal() { AssertIsOnOwningThread(); MOZ_ASSERT(!mTemporaryStrongMutableFile); if (mMutableFile) { mMutableFile->ClearBackgroundActor(); mMutableFile = nullptr; MOZ_ALWAYS_TRUE(PBackgroundMutableFileChild::SendDeleteMe()); } } void BackgroundMutableFileChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); if (mMutableFile) { mMutableFile->ClearBackgroundActor(); #ifdef DEBUG mMutableFile = nullptr; #endif } } PBackgroundFileHandleChild* BackgroundMutableFileChild::AllocPBackgroundFileHandleChild( const FileMode& aMode) { MOZ_CRASH("PBackgroundFileHandleChild actors should be manually " "constructed!"); } bool BackgroundMutableFileChild::DeallocPBackgroundFileHandleChild( PBackgroundFileHandleChild* aActor) { AssertIsOnOwningThread(); MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } /******************************************************************************* * 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>& 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>* 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& aResponse) { AssertIsOnOwningThread(); ResultHelper helper(mRequest, mTransaction, &aResponse); DispatchSuccessEvent(&helper); } void BackgroundRequestChild::HandleResponse( const SerializedStructuredCloneReadInfo& aResponse) { AssertIsOnOwningThread(); // XXX Fix this somehow... auto& serializedCloneInfo = const_cast(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& 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(); // Move relevant data into the cloneReadInfo *cloneReadInfo = Move(serializedCloneInfo); // Get the files nsTArray files; DeserializeStructuredCloneFiles(database, serializedCloneInfo.files(), GetNextModuleSet(*cloneReadInfo), files); cloneReadInfo->mFiles = Move(files); } } ResultHelper helper(mRequest, mTransaction, &cloneReadInfos); DispatchSuccessEvent(&helper); } void BackgroundRequestChild::HandleResponse(JS::Handle 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 files; DeserializeStructuredCloneFiles(database, aPreprocessInfo.files(), nullptr, files); RefPtr& 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& 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 files; DeserializeStructuredCloneFiles(database, preprocessInfo.files(), nullptr, files); RefPtr& 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 = mPreprocessHelpers[index]; if (preprocessHelper) { preprocessHelper->ClearActor(); preprocessHelper = nullptr; } } if (mTransaction) { mTransaction->AssertIsOnOwningThread(); mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ aWhy == Deletion); #ifdef DEBUG mTransaction = nullptr; #endif } } mozilla::ipc::IPCResult 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 IPC_OK(); } mozilla::ipc::IPCResult 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))) { if (!SendContinue(rv)) { return IPC_FAIL_NO_REASON(this); } return IPC_OK(); } return IPC_OK(); } nsresult BackgroundRequestChild:: PreprocessHelper::Init(const nsTArray& 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 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 bytecodeStream; bytecodeFile.mBlob->CreateInputStream(getter_AddRefs(bytecodeStream), errorResult); if (NS_WARN_IF(errorResult.Failed())) { return errorResult.StealNSResult(); } nsCOMPtr compiledStream; compiledFile.mBlob->CreateInputStream(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(); if (!mTaskQueue) { // 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 target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); MOZ_ASSERT(target); // We use a TaskQueue here in order to be sure that the events are // dispatched in the correct order. This is not guaranteed in case we use // the I/O thread directly. mTaskQueue = new TaskQueue(target.forget()); mTaskQueueEventTarget = mTaskQueue->WrapAsEventTarget(); } nsresult rv = mTaskQueueEventTarget->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); } } } void BackgroundRequestChild:: PreprocessHelper::ProcessCurrentStreamPair() { MOZ_ASSERT(!IsOnOwningThread()); MOZ_ASSERT(!mStreamPairs.IsEmpty()); nsresult rv; const StreamPair& streamPair = mStreamPairs[0]; // We still don't have the current bytecode FileDesc. if (!mCurrentBytecodeFileDesc) { const nsCOMPtr& bytecodeStream = streamPair.first; MOZ_ASSERT(bytecodeStream); mCurrentBytecodeFileDesc = GetFileDescriptorFromStream(bytecodeStream); if (!mCurrentBytecodeFileDesc) { rv = WaitForStreamReady(bytecodeStream); if (NS_WARN_IF(NS_FAILED(rv))) { ContinueWithStatus(rv); } return; } } if (!mCurrentCompiledFileDesc) { const nsCOMPtr& compiledStream = streamPair.second; MOZ_ASSERT(compiledStream); mCurrentCompiledFileDesc = GetFileDescriptorFromStream(compiledStream); if (!mCurrentCompiledFileDesc) { rv = WaitForStreamReady(compiledStream); if (NS_WARN_IF(NS_FAILED(rv))) { ContinueWithStatus(rv); } return; } } MOZ_ASSERT(mCurrentBytecodeFileDesc && mCurrentCompiledFileDesc); JS::BuildIdCharVector buildId; bool ok = GetBuildId(&buildId); if (NS_WARN_IF(!ok)) { ContinueWithStatus(NS_ERROR_FAILURE); return; } RefPtr module = JS::DeserializeWasmModule(mCurrentBytecodeFileDesc, mCurrentCompiledFileDesc, Move(buildId), nullptr, 0, 0); if (NS_WARN_IF(!module)) { ContinueWithStatus(NS_ERROR_FAILURE); return; } mModuleSet.AppendElement(module); mStreamPairs.RemoveElementAt(0); ContinueWithStatus(NS_OK); } nsresult BackgroundRequestChild:: PreprocessHelper::WaitForStreamReady(nsIInputStream* aInputStream) { MOZ_ASSERT(!IsOnOwningThread()); MOZ_ASSERT(aInputStream); nsCOMPtr asyncFileMetadata = do_QueryInterface(aInputStream); if (asyncFileMetadata) { nsresult rv = asyncFileMetadata->AsyncFileMetadataWait(this, mTaskQueueEventTarget); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } nsCOMPtr asyncStream = do_QueryInterface(aInputStream); if (!asyncStream) { return NS_ERROR_NO_INTERFACE; } nsresult rv = asyncStream->AsyncWait(this, 0, 0, mTaskQueueEventTarget); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } void BackgroundRequestChild:: PreprocessHelper::ContinueWithStatus(nsresult aStatus) { MOZ_ASSERT(!IsOnOwningThread()); // Let's reset the value for the next operation. mCurrentBytecodeFileDesc = nullptr; mCurrentCompiledFileDesc = nullptr; nsCOMPtr eventTarget; if (NS_WARN_IF(NS_FAILED(aStatus))) { // If the previous operation failed, we don't continue the processing of the // other stream pairs. MOZ_ASSERT(mResultCode == NS_OK); mResultCode = aStatus; eventTarget = mOwningEventTarget; } else if (mStreamPairs.IsEmpty()) { // If all the streams have been processed, we can go back to the owning // thread. eventTarget = mOwningEventTarget; } else { // Continue the processing. eventTarget = mTaskQueueEventTarget; } nsresult rv = eventTarget->Dispatch(this, NS_DISPATCH_NORMAL); Unused << NS_WARN_IF(NS_FAILED(rv)); } NS_IMPL_ISUPPORTS_INHERITED(BackgroundRequestChild::PreprocessHelper, CancelableRunnable, nsIInputStreamCallback, nsIFileMetadataCallback) NS_IMETHODIMP BackgroundRequestChild:: PreprocessHelper::Run() { if (IsOnOwningThread()) { RunOnOwningThread(); } else { ProcessCurrentStreamPair(); } return NS_OK; } NS_IMETHODIMP BackgroundRequestChild:: PreprocessHelper::OnInputStreamReady(nsIAsyncInputStream* aStream) { return DataIsReady(aStream); } NS_IMETHODIMP BackgroundRequestChild:: PreprocessHelper::OnFileMetadataReady(nsIAsyncFileMetadata* aObject) { nsCOMPtr stream = do_QueryInterface(aObject); MOZ_ASSERT(stream, "It was a stream before!"); return DataIsReady(stream); } nsresult BackgroundRequestChild:: PreprocessHelper::DataIsReady(nsIInputStream* aStream) { MOZ_ASSERT(!IsOnOwningThread()); MOZ_ASSERT(aStream); MOZ_ASSERT(!mStreamPairs.IsEmpty()); // We still don't have the current bytecode FileDesc. if (!mCurrentBytecodeFileDesc) { mCurrentBytecodeFileDesc = GetFileDescriptorFromStream(aStream); if (!mCurrentBytecodeFileDesc) { ContinueWithStatus(NS_ERROR_FAILURE); return NS_OK; } // Let's continue with the processing of the current pair. ProcessCurrentStreamPair(); return NS_OK; } if (!mCurrentCompiledFileDesc) { mCurrentCompiledFileDesc = GetFileDescriptorFromStream(aStream); if (!mCurrentCompiledFileDesc) { ContinueWithStatus(NS_ERROR_FAILURE); return NS_OK; } // Let's continue with the processing of the current pair. ProcessCurrentStreamPair(); return NS_OK; } MOZ_CRASH("If we have both fileDescs why are we here?"); } 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 mRequest; ActionFunc mActionFunc; public: explicit DelayedActionRunnable(BackgroundCursorChild* aActor, ActionFunc aActionFunc) : CancelableRunnable("indexedDB::BackgroundCursorChild::DelayedActionRunnable") , 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); } 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); } BackgroundCursorChild::~BackgroundCursorChild() { MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild); } 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 deleteRunnable = new DelayedActionRunnable( this, &BackgroundCursorChild::SendDeleteMeInternal); MOZ_ALWAYS_SUCCEEDS(this->GetActorEventTarget()-> Dispatch(deleteRunnable.forget(), NS_DISPATCH_NORMAL)); } } void BackgroundCursorChild::HandleResponse( const nsTArray& 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&>(aResponses); for (ObjectStoreCursorResponse& response : responses) { StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); cloneReadInfo.mDatabase = mTransaction->Database(); DeserializeStructuredCloneFiles(mTransaction->Database(), response.cloneInfo().files(), nullptr, cloneReadInfo.mFiles); RefPtr 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(aResponse); RefPtr 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(aResponse); StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); cloneReadInfo.mDatabase = mTransaction->Database(); DeserializeStructuredCloneFiles(mTransaction->Database(), aResponse.cloneInfo().files(), nullptr, cloneReadInfo.mFiles); RefPtr 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(aResponse); RefPtr 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 } mozilla::ipc::IPCResult 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 request; mStrongRequest.swap(request); RefPtr 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 IPC_OK(); } 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; } /******************************************************************************* * BackgroundFileHandleChild ******************************************************************************/ BackgroundFileHandleChild::BackgroundFileHandleChild(IDBFileHandle* aFileHandle) : mTemporaryStrongFileHandle(aFileHandle) , mFileHandle(aFileHandle) { MOZ_ASSERT(aFileHandle); aFileHandle->AssertIsOnOwningThread(); MOZ_COUNT_CTOR(BackgroundFileHandleChild); } BackgroundFileHandleChild::~BackgroundFileHandleChild() { AssertIsOnOwningThread(); MOZ_COUNT_DTOR(BackgroundFileHandleChild); } #ifdef DEBUG void BackgroundFileHandleChild::AssertIsOnOwningThread() const { static_cast(Manager())->AssertIsOnOwningThread(); } #endif // DEBUG void BackgroundFileHandleChild::SendDeleteMeInternal() { AssertIsOnOwningThread(); if (mFileHandle) { NoteActorDestroyed(); MOZ_ALWAYS_TRUE(PBackgroundFileHandleChild::SendDeleteMe()); } } void BackgroundFileHandleChild::NoteActorDestroyed() { AssertIsOnOwningThread(); MOZ_ASSERT_IF(mTemporaryStrongFileHandle, mFileHandle); if (mFileHandle) { mFileHandle->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. mTemporaryStrongFileHandle = nullptr; mFileHandle = nullptr; } } void BackgroundFileHandleChild::NoteComplete() { AssertIsOnOwningThread(); MOZ_ASSERT_IF(mFileHandle, mTemporaryStrongFileHandle); mTemporaryStrongFileHandle = nullptr; } void BackgroundFileHandleChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); NoteActorDestroyed(); } mozilla::ipc::IPCResult BackgroundFileHandleChild::RecvComplete(const bool& aAborted) { AssertIsOnOwningThread(); MOZ_ASSERT(mFileHandle); mFileHandle->FireCompleteOrAbortEvents(aAborted); NoteComplete(); return IPC_OK(); } PBackgroundFileRequestChild* BackgroundFileHandleChild::AllocPBackgroundFileRequestChild( const FileRequestParams& aParams) { MOZ_CRASH("PBackgroundFileRequestChild actors should be manually " "constructed!"); } bool BackgroundFileHandleChild::DeallocPBackgroundFileRequestChild( PBackgroundFileRequestChild* aActor) { MOZ_ASSERT(aActor); delete static_cast(aActor); return true; } /******************************************************************************* * BackgroundFileRequestChild ******************************************************************************/ BackgroundFileRequestChild::BackgroundFileRequestChild( IDBFileRequest* aFileRequest) : mFileRequest(aFileRequest) , mFileHandle(aFileRequest->GetFileHandle()) , mActorDestroyed(false) { MOZ_ASSERT(aFileRequest); aFileRequest->AssertIsOnOwningThread(); MOZ_ASSERT(mFileHandle); mFileHandle->AssertIsOnOwningThread(); MOZ_COUNT_CTOR(BackgroundFileRequestChild); } BackgroundFileRequestChild::~BackgroundFileRequestChild() { AssertIsOnOwningThread(); MOZ_ASSERT(!mFileHandle); MOZ_COUNT_DTOR(BackgroundFileRequestChild); } #ifdef DEBUG void BackgroundFileRequestChild::AssertIsOnOwningThread() const { MOZ_ASSERT(mFileRequest); mFileRequest->AssertIsOnOwningThread(); } #endif // DEBUG void BackgroundFileRequestChild::HandleResponse(nsresult aResponse) { AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(aResponse)); MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_FILEHANDLE); MOZ_ASSERT(mFileHandle); DispatchFileHandleErrorEvent(mFileRequest, aResponse, mFileHandle); } void BackgroundFileRequestChild::HandleResponse( const FileRequestGetFileResponse& aResponse) { AssertIsOnOwningThread(); RefPtr file = ConvertActorToFile(mFileHandle, aResponse); FileHandleResultHelper helper(mFileRequest, mFileHandle, file); DispatchFileHandleSuccessEvent(&helper); } void BackgroundFileRequestChild::HandleResponse(const nsCString& aResponse) { AssertIsOnOwningThread(); FileHandleResultHelper helper(mFileRequest, mFileHandle, &aResponse); DispatchFileHandleSuccessEvent(&helper); } void BackgroundFileRequestChild::HandleResponse(const FileRequestMetadata& aResponse) { AssertIsOnOwningThread(); FileHandleResultHelper helper(mFileRequest, mFileHandle, &aResponse); DispatchFileHandleSuccessEvent(&helper); } void BackgroundFileRequestChild::HandleResponse(JS::Handle aResponse) { AssertIsOnOwningThread(); FileHandleResultHelper helper(mFileRequest, mFileHandle, &aResponse); DispatchFileHandleSuccessEvent(&helper); } void BackgroundFileRequestChild::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); MOZ_ASSERT(!mActorDestroyed); mActorDestroyed = true; if (mFileHandle) { mFileHandle->AssertIsOnOwningThread(); mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ aWhy == Deletion); #ifdef DEBUG mFileHandle = nullptr; #endif } } mozilla::ipc::IPCResult BackgroundFileRequestChild::Recv__delete__(const FileRequestResponse& aResponse) { AssertIsOnOwningThread(); MOZ_ASSERT(mFileRequest); MOZ_ASSERT(mFileHandle); if (mFileHandle->IsAborted()) { // Always handle an "error" with ABORT_ERR if the file handle was aborted, // even if the request succeeded or failed with another error. HandleResponse(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR); } else { switch (aResponse.type()) { case FileRequestResponse::Tnsresult: HandleResponse(aResponse.get_nsresult()); break; case FileRequestResponse::TFileRequestGetFileResponse: HandleResponse(aResponse.get_FileRequestGetFileResponse()); break; case FileRequestResponse::TFileRequestReadResponse: HandleResponse(aResponse.get_FileRequestReadResponse().data()); break; case FileRequestResponse::TFileRequestWriteResponse: HandleResponse(JS::UndefinedHandleValue); break; case FileRequestResponse::TFileRequestTruncateResponse: HandleResponse(JS::UndefinedHandleValue); break; case FileRequestResponse::TFileRequestFlushResponse: HandleResponse(JS::UndefinedHandleValue); break; case FileRequestResponse::TFileRequestGetMetadataResponse: HandleResponse(aResponse.get_FileRequestGetMetadataResponse() .metadata()); break; default: MOZ_CRASH("Unknown response type!"); } } mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true); // Null this out so that we don't try to call OnRequestFinished() again in // ActorDestroy. mFileHandle = nullptr; return IPC_OK(); } mozilla::ipc::IPCResult BackgroundFileRequestChild::RecvProgress(const uint64_t& aProgress, const uint64_t& aProgressMax) { AssertIsOnOwningThread(); MOZ_ASSERT(mFileRequest); mFileRequest->FireProgressEvent(aProgress, aProgressMax); return IPC_OK(); } /******************************************************************************* * BackgroundUtilsChild ******************************************************************************/ BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager) : mManager(aManager) { AssertIsOnOwningThread(); MOZ_ASSERT(aManager); MOZ_COUNT_CTOR(indexedDB::BackgroundUtilsChild); } BackgroundUtilsChild::~BackgroundUtilsChild() { MOZ_COUNT_DTOR(indexedDB::BackgroundUtilsChild); } 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