Dactyloidae/dom/indexedDB/ActorsChild.cpp
Martok f059bb0a59 Issue #2240 - Align Microtasks and promises scheduling with spec
Microtasks, resolved Promises and Observers are handled after the sync
task that caused them, in the order they were generated.
Also simplifies reentrancy handling.

Based-on: m-c 1193394
2024-01-09 11:59:32 +08:00

3601 lines
93 KiB
C++

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