Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/indexedDB/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 86 kB image not shown  

Quelle  ActorsChild.cpp   Sprache: C

 
/* -*- 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 <type_traits>

#include "ActorsChild.h"
#include "BackgroundChildImpl.h"
#include "IDBDatabase.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBIndex.h"
#include "IDBObjectStore.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
#include "IndexedDatabase.h"
#include "IndexedDatabaseInlines.h"
#include "IndexedDBCommon.h"
#include "js/Array.h"               // JS::NewArrayObject, JS::SetArrayLength
#include "js/Date.h"                // JS::NewDateObject, JS::TimeClip
#include "js/PropertyAndElement.h"  // JS_DefineElement, JS_DefineProperty
#include <mozIRemoteLazyInputStream.h>
#include "mozilla/ArrayAlgorithm.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/Maybe.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/dom/quota/ResultExtensions.h"
#include "mozilla/Encoding.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/TaskQueue.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsIAsyncInputStream.h"
#include "nsIEventTarget.h"
#include "nsIFileStreams.h"
#include "nsNetCID.h"
#include "nsPIDOMWindow.h"
#include "nsThreadUtils.h"
#include "nsTraceRefcnt.h"
#include "ProfilerHelpers.h"
#include "ReportInternalError.h"
#include "ThreadLocal.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::indexedDB {

/*******************************************************************************
 * ThreadLocal
 ******************************************************************************/


ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId)
    : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1),
      mLoggingIdString(aBackgroundChildLoggingId) {
  MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal);
}

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(CCReason::API);
#endif  // BUILD_GC_ON_IPC_MESSAGES
}

class MOZ_STACK_CLASS AutoSetCurrentTransaction final {
  using BackgroundChildImpl = mozilla::ipc::BackgroundChildImpl;

  Maybe<IDBTransaction&> const mTransaction;
  Maybe<IDBTransaction&> mPreviousTransaction;
  ThreadLocal* mThreadLocal;

 public:
  AutoSetCurrentTransaction(const AutoSetCurrentTransaction&) = delete;
  AutoSetCurrentTransaction(AutoSetCurrentTransaction&&) = delete;
  AutoSetCurrentTransaction& operator=(const AutoSetCurrentTransaction&) =
      delete;
  AutoSetCurrentTransaction& operator=(AutoSetCurrentTransaction&&) = delete;

  explicit AutoSetCurrentTransaction(Maybe<IDBTransaction&> aTransaction)
      : mTransaction(aTransaction), mThreadLocal(nullptr) {
    if (aTransaction) {
      BackgroundChildImpl::ThreadLocal* threadLocal =
          BackgroundChildImpl::GetThreadLocalForCurrentThread();
      MOZ_ASSERT(threadLocal);

      // Hang onto this for resetting later.
      mThreadLocal = threadLocal->mIndexedDBThreadLocal.get();
      MOZ_ASSERT(mThreadLocal);

      // Save the current value.
      mPreviousTransaction = mThreadLocal->MaybeCurrentTransactionRef();

      // Set the new value.
      mThreadLocal->SetCurrentTransaction(aTransaction);
    }
  }

  ~AutoSetCurrentTransaction() {
    MOZ_ASSERT_IF(mThreadLocal, mTransaction);
    MOZ_ASSERT_IF(mThreadLocal,
                  ReferenceEquals(mThreadLocal->MaybeCurrentTransactionRef(),
                                  mTransaction));

    if (mThreadLocal) {
      // Reset old value.
      mThreadLocal->SetCurrentTransaction(mPreviousTransaction);
    }
  }
};

template <typename T>
void SetResultAndDispatchSuccessEvent(
    const NotNull<RefPtr<IDBRequest>>& aRequest,
    const SafeRefPtr<IDBTransaction>& aTransaction, T& aPtr,
    RefPtr<Event> aEvent = nullptr);

namespace detail {
void DispatchSuccessEvent(const NotNull<RefPtr<IDBRequest>>& aRequest,
                          const SafeRefPtr<IDBTransaction>& aTransaction,
                          const RefPtr<Event>& aEvent);

template <class T>
std::enable_if_t<std::is_same_v<T, IDBDatabase> || std::is_same_v<T, IDBCursor>,
                 nsresult>
GetResult(JSContext* aCx, T* aDOMObject, JS::MutableHandle<JS::Value> aResult) {
  if (!aDOMObject) {
    aResult.setNull();
    return NS_OK;
  }

  const 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, const JS::Handle<JS::Value>* aValue,
                   JS::MutableHandle<JS::Value> aResult) {
  aResult.set(*aValue);
  return NS_OK;
}

nsresult GetResult(JSContext* aCx, const uint64_t* aValue,
                   JS::MutableHandle<JS::Value> aResult) {
  aResult.set(JS::NumberValue(*aValue));
  return NS_OK;
}

nsresult GetResult(JSContext* aCx, StructuredCloneReadInfoChild&& aCloneInfo,
                   JS::MutableHandle<JS::Value> aResult) {
  const bool ok =
      IDBObjectStore::DeserializeValue(aCx, std::move(aCloneInfo), aResult);

  if (NS_WARN_IF(!ok)) {
    return NS_ERROR_DOM_DATA_CLONE_ERR;
  }

  return NS_OK;
}

nsresult GetResult(JSContext* aCx, StructuredCloneReadInfoChild* aCloneInfo,
                   JS::MutableHandle<JS::Value> aResult) {
  return GetResult(aCx, std::move(*aCloneInfo), aResult);
}

nsresult GetResult(JSContext* aCx,
                   nsTArray<StructuredCloneReadInfoChild>* 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 = aCloneInfos->ElementAt(index);

      JS::Rooted<JS::Value> value(aCx);

      const nsresult rv = GetResult(aCx, std::move(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) {
  const 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);

      const 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;
}
}  // namespace detail

auto DeserializeStructuredCloneFiles(
    IDBDatabase* aDatabase,
    const nsTArray<SerializedStructuredCloneFile>& aSerializedFiles,
    bool aForPreprocess) {
  MOZ_ASSERT_IF(aForPreprocess, aSerializedFiles.Length() == 1);

  return TransformIntoNewArray(
      aSerializedFiles,
      [aForPreprocess, &database = *aDatabase](
          const auto& serializedFile) -> StructuredCloneFileChild {
        MOZ_ASSERT_IF(
            aForPreprocess,
            serializedFile.type() == StructuredCloneFileBase::eStructuredClone);

        const NullableBlob& blob = serializedFile.file();

        switch (serializedFile.type()) {
          case StructuredCloneFileBase::eBlob: {
            MOZ_ASSERT(blob.type() == NullableBlob::TIPCBlob);

            const IPCBlob& ipcBlob = blob.get_IPCBlob();

            const RefPtr<BlobImpl> blobImpl =
                IPCBlobUtils::Deserialize(ipcBlob);
            MOZ_ASSERT(blobImpl);

            RefPtr<Blob> blob =
                Blob::Create(database.GetOwnerGlobal(), blobImpl);
            MOZ_ASSERT(blob);

            return {StructuredCloneFileBase::eBlob, std::move(blob)};
          }

          case StructuredCloneFileBase::eStructuredClone: {
            if (aForPreprocess) {
              MOZ_ASSERT(blob.type() == NullableBlob::TIPCBlob);

              const IPCBlob& ipcBlob = blob.get_IPCBlob();

              const RefPtr<BlobImpl> blobImpl =
                  IPCBlobUtils::Deserialize(ipcBlob);
              MOZ_ASSERT(blobImpl);

              RefPtr<Blob> blob =
                  Blob::Create(database.GetOwnerGlobal(), blobImpl);
              MOZ_ASSERT(blob);

              return {StructuredCloneFileBase::eStructuredClone,
                      std::move(blob)};
            }
            MOZ_ASSERT(blob.type() == NullableBlob::Tnull_t);

            return StructuredCloneFileChild{
                StructuredCloneFileBase::eStructuredClone};
          }

          case StructuredCloneFileBase::eMutableFile:
          case StructuredCloneFileBase::eWasmBytecode:
          case StructuredCloneFileBase::eWasmCompiled: {
            MOZ_ASSERT(blob.type() == NullableBlob::Tnull_t);

            return StructuredCloneFileChild{serializedFile.type()};

            // Don't set mBlob, support for storing WebAssembly.Modules has been
            // removed in bug 1469395. Support for de-serialization of
            // WebAssembly.Modules has been removed in bug 1561876. Support for
            // MutableFile has been removed in bug 1500343. Full removal is
            // tracked in bug 1487479.
          }

          default:
            MOZ_CRASH("Should never get here!");
        }
      });
}

JSStructuredCloneData PreprocessingNotSupported() {
  MOZ_CRASH("Preprocessing not (yet) supported!");
}

template <typename PreprocessInfoAccessor>
StructuredCloneReadInfoChild DeserializeStructuredCloneReadInfo(
    SerializedStructuredCloneReadInfo&& aSerialized,
    IDBDatabase* const aDatabase,
    PreprocessInfoAccessor preprocessInfoAccessor) {
  // XXX Make this a class invariant of SerializedStructuredCloneReadInfo.
  MOZ_ASSERT_IF(aSerialized.hasPreprocessInfo(),
                0 == aSerialized.data().data.Size());
  return {aSerialized.hasPreprocessInfo() ? preprocessInfoAccessor()
                                          : std::move(aSerialized.data().data),
          DeserializeStructuredCloneFiles(aDatabase, aSerialized.files(),
                                          /* aForPreprocess */ false),
          aDatabase};
}

// TODO: Remove duplication between DispatchErrorEvent and DispatchSucessEvent.

void DispatchErrorEvent(
    MovingNotNull<RefPtr<IDBRequest>> aRequest, nsresult aErrorCode,
    const SafeRefPtr<IDBTransaction>& aTransaction = nullptr,
    RefPtr<Event> aEvent = nullptr) {
  const RefPtr<IDBRequest> request = std::move(aRequest);

  request->AssertIsOnOwningThread();
  MOZ_ASSERT(NS_FAILED(aErrorCode));
  MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);

  AUTO_PROFILER_LABEL("IndexedDB:DispatchErrorEvent", DOM);

  request->SetError(aErrorCode);

  if (!aEvent) {
    // Make an error event and fire it at the target.
    aEvent = CreateGenericEvent(request, nsDependentString(kErrorEventType),
                                eDoesBubble, eCancelable);
  }
  MOZ_ASSERT(aEvent);

  // XXX This is redundant if we are called from
  // DispatchSuccessEvent.
  Maybe<AutoSetCurrentTransaction> asct;
  if (aTransaction) {
    asct.emplace(SomeRef(*aTransaction));
  }

  if (aTransaction && aTransaction->IsInactive()) {
    aTransaction->TransitionToActive();
  }

  if (aTransaction) {
    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
        "Firing %s event with error 0x%x""%s (0x%" PRIx32 ")",
        aTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
        IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
        static_cast<uint32_t>(aErrorCode));
  } else {
    IDB_LOG_MARK_CHILD_REQUEST("Firing %s event with error 0x%x",
                               "%s (0x%" PRIx32 ")",
                               request->LoggingSerialNumber(),
                               IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
                               static_cast<uint32_t>(aErrorCode));
  }

  IgnoredErrorResult rv;
  const bool doDefault =
      request->DispatchEvent(*aEvent, CallerType::System, rv);
  if (NS_WARN_IF(rv.Failed())) {
    return;
  }

  MOZ_ASSERT(!aTransaction || aTransaction->IsActive() ||
             aTransaction->IsAborted() ||
             aTransaction->WasExplicitlyCommitted());

  if (aTransaction && aTransaction->IsActive()) {
    aTransaction->TransitionToInactive();

    // 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 (aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) {
      WidgetEvent* const internalEvent = aEvent->WidgetEventPtr();
      MOZ_ASSERT(internalEvent);

      if (internalEvent->mFlags.mExceptionWasRaised) {
        aTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
      } else if (doDefault) {
        aTransaction->Abort(request);
      }
    }
  }
}

template <typename T>
void SetResultAndDispatchSuccessEvent(
    const NotNull<RefPtr<IDBRequest>>& aRequest,
    const SafeRefPtr<IDBTransaction>& aTransaction, T& aPtr,
    RefPtr<Event> aEvent) {
  const auto autoTransaction =
      AutoSetCurrentTransaction{aTransaction.maybeDeref()};

  AUTO_PROFILER_LABEL("IndexedDB:SetResultAndDispatchSuccessEvent", DOM);

  aRequest->AssertIsOnOwningThread();

  if (aTransaction && aTransaction->IsAborted()) {
    DispatchErrorEvent(aRequest, aTransaction->AbortCode(), aTransaction);
    return;
  }

  if (!aEvent) {
    aEvent =
        CreateGenericEvent(aRequest.get(), nsDependentString(kSuccessEventType),
                           eDoesNotBubble, eNotCancelable);
  }
  MOZ_ASSERT(aEvent);

  aRequest->SetResult(
      [&aPtr](JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
        MOZ_ASSERT(aCx);
        return detail::GetResult(aCx, &aPtr, aResult);
      });

  detail::DispatchSuccessEvent(aRequest, aTransaction, aEvent);
}

namespace detail {
void DispatchSuccessEvent(const NotNull<RefPtr<IDBRequest>>& aRequest,
                          const SafeRefPtr<IDBTransaction>& aTransaction,
                          const RefPtr<Event>& aEvent) {
  if (aTransaction && aTransaction->IsInactive()) {
    aTransaction->TransitionToActive();
  }

  if (aTransaction) {
    IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
        "Firing %s event""%s", aTransaction->LoggingSerialNumber(),
        aRequest->LoggingSerialNumber(),
        IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
  } else {
    IDB_LOG_MARK_CHILD_REQUEST("Firing %s event""%s",
                               aRequest->LoggingSerialNumber(),
                               IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
  }

  MOZ_ASSERT_IF(aTransaction && !aTransaction->WasExplicitlyCommitted(),
                aTransaction->IsActive() && !aTransaction->IsAborted());

  IgnoredErrorResult rv;
  aRequest->DispatchEvent(*aEvent, rv);
  if (NS_WARN_IF(rv.Failed())) {
    return;
  }

  WidgetEvent* const internalEvent = aEvent->WidgetEventPtr();
  MOZ_ASSERT(internalEvent);

  if (aTransaction && aTransaction->IsActive()) {
    aTransaction->TransitionToInactive();

    if (internalEvent->mFlags.mExceptionWasRaised) {
      aTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
    } else {
      // To handle upgrade transaction.
      aTransaction->CommitIfNotStarted();
    }
  }
}
}  // namespace detail

PRFileDesc* GetFileDescriptorFromStream(nsIInputStream* aStream) {
  MOZ_ASSERT(aStream);

  const nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
  if (NS_WARN_IF(!fileMetadata)) {
    return nullptr;
  }

  PRFileDesc* fileDesc;
  const nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  MOZ_ASSERT(fileDesc);

  return fileDesc;
}

auto GetKeyOperator(const IDBCursorDirection aDirection) {
  switch (aDirection) {
    case IDBCursorDirection::Next:
    case IDBCursorDirection::Nextunique:
      return &Key::operator>=;
    case IDBCursorDirection::Prev:
    case IDBCursorDirection::Prevunique:
      return &Key::operator<=;
    default:
      MOZ_CRASH("Should never get here.");
  }
}

// Does not need to be threadsafe since this only runs on one thread, but
// inheriting from CancelableRunnable is easy.
template <typename T>
class DelayedActionRunnable final : public CancelableRunnable {
  using ActionFunc = void (T::*)();

  SafeRefPtr<T> mActor;
  RefPtr<IDBRequest> mRequest;
  ActionFunc mActionFunc;

 public:
  explicit DelayedActionRunnable(SafeRefPtr<T> aActor, ActionFunc aActionFunc)
      : CancelableRunnable("indexedDB::DelayedActionRunnable"),
        mActor(std::move(aActor)),
        mRequest(mActor->GetRequest()),
        mActionFunc(aActionFunc) {
    MOZ_ASSERT(mActor);
    mActor->AssertIsOnOwningThread();
    MOZ_ASSERT(mRequest);
    MOZ_ASSERT(mActionFunc);
  }

 private:
  ~DelayedActionRunnable() = default;

  NS_DECL_NSIRUNNABLE
  nsresult Cancel() override;
};

}  // namespace

/*******************************************************************************
 * Actor class declarations
 ******************************************************************************/


// DiscardableRunnable is used to make workers happy.
class BackgroundRequestChild::PreprocessHelper final
    : public DiscardableRunnable,
      public nsIInputStreamCallback,
      public nsIFileMetadataCallback {
  enum class State {
    // Just created on the owning thread, dispatched to the thread pool. Next
    // step is either Finishing if stream was ready to be read or
    // WaitingForStreamReady if the stream is not ready.
    Initial,

    // Waiting for stream to be ready on a thread pool thread. Next state is
    // Finishing.
    WaitingForStreamReady,

    // Waiting to finish/finishing on the owning thread. Next step is Completed.
    Finishing,

    // All done.
    Completed
  };

  const nsCOMPtr<nsIEventTarget> mOwningEventTarget;
  RefPtr<TaskQueue> mTaskQueue;
  nsCOMPtr<nsIInputStream> mStream;
  UniquePtr<JSStructuredCloneData> mCloneData;
  BackgroundRequestChild* mActor;
  const uint32_t mCloneDataIndex;
  nsresult mResultCode;
  State mState;

 public:
  PreprocessHelper(uint32_t aCloneDataIndex, BackgroundRequestChild* aActor)
      : DiscardableRunnable(
            "indexedDB::BackgroundRequestChild::PreprocessHelper"),
        mOwningEventTarget(aActor->GetActorEventTarget()),
        mActor(aActor),
        mCloneDataIndex(aCloneDataIndex),
        mResultCode(NS_OK),
        mState(State::Initial) {
    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 StructuredCloneFileChild& aFile);

  nsresult Dispatch();

 private:
  ~PreprocessHelper() {
    MOZ_ASSERT(mState == State::Initial || mState == State::Completed);

    if (mTaskQueue) {
      mTaskQueue->BeginShutdown();
    }
  }

  nsresult Start();

  nsresult ProcessStream();

  void Finish();

  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSIRUNNABLE
  NS_DECL_NSIINPUTSTREAMCALLBACK
  NS_DECL_NSIFILEMETADATACALLBACK
};

/*******************************************************************************
 * BackgroundRequestChildBase
 ******************************************************************************/


BackgroundRequestChildBase::BackgroundRequestChildBase(
    MovingNotNull<RefPtr<IDBRequest>> aRequest)
    : mRequest(std::move(aRequest)) {
  mRequest->AssertIsOnOwningThread();

  MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase);
}

BackgroundRequestChildBase::~BackgroundRequestChildBase() {
  AssertIsOnOwningThread();

  MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase);
}

#ifdef DEBUG

void BackgroundRequestChildBase::AssertIsOnOwningThread() const {
  mRequest->AssertIsOnOwningThread();
}

#endif  // DEBUG

/*******************************************************************************
 * BackgroundFactoryChild
 ******************************************************************************/


BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory& aFactory)
    : mFactory(&aFactory) {
  AssertIsOnOwningThread();
  mFactory->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<BackgroundFactoryRequestChild*>(aActor);
  return true;
}

already_AddRefed<PBackgroundIDBDatabaseChild>
BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild(
    const DatabaseSpec& aSpec,
    PBackgroundIDBFactoryRequestChild* aRequest) const {
  AssertIsOnOwningThread();

  autoconst request = static_cast<BackgroundFactoryRequestChild*>(aRequest);
  MOZ_ASSERT(request);

  RefPtr<BackgroundDatabaseChild> actor =
      new BackgroundDatabaseChild(aSpec, request);
  return actor.forget();
}

mozilla::ipc::IPCResult
BackgroundFactoryChild::RecvPBackgroundIDBDatabaseConstructor(
    PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec,
    NotNull<PBackgroundIDBFactoryRequestChild*> aRequest) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(aActor);

  return IPC_OK();
}

/*******************************************************************************
 * BackgroundFactoryRequestChild
 ******************************************************************************/


BackgroundFactoryRequestChild::BackgroundFactoryRequestChild(
    SafeRefPtr<IDBFactory> aFactory,
    MovingNotNull<RefPtr<IDBOpenDBRequest>> aOpenRequest, bool aIsDeleteOp,
    uint64_t aRequestedVersion)
    : BackgroundRequestChildBase(std::move(aOpenRequest)),
      mFactory(std::move(aFactory)),
      mDatabaseActor(nullptr),
      mRequestedVersion(aRequestedVersion),
      mIsDeleteOp(aIsDeleteOp) {
  // Can't assert owning thread here because IPDL has not yet set our manager!
  MOZ_ASSERT(mFactory);
  mFactory->AssertIsOnOwningThread();

  MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild);
}

BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() {
  MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild);
}

NotNull<IDBOpenDBRequest*> BackgroundFactoryRequestChild::GetOpenDBRequest()
    const {
  AssertIsOnOwningThread();

  // XXX NotNull might provide something to encapsulate this
  return WrapNotNullUnchecked(
      static_cast<IDBOpenDBRequest*>(mRequest.get().get()));
}

void BackgroundFactoryRequestChild::SetDatabaseActor(
    BackgroundDatabaseChild* aActor) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(!aActor || !mDatabaseActor);

  mDatabaseActor = aActor;
}

void 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);
  }
}

void BackgroundFactoryRequestChild::HandleResponse(
    const OpenDatabaseRequestResponse& aResponse) {
  AssertIsOnOwningThread();

  mRequest->Reset();

  auto* databaseActor = static_cast<BackgroundDatabaseChild*>(
      aResponse.database().AsChild().get());
  MOZ_ASSERT(databaseActor);

  IDBDatabase* const database = [this, databaseActor]() -> IDBDatabase* {
    IDBDatabase* database = databaseActor->GetDOMObject();
    if (!database) {
      Unused << this;

      if (NS_WARN_IF(!databaseActor->EnsureDOMObject())) {
        return nullptr;
      }
      MOZ_ASSERT(mDatabaseActor);

      database = databaseActor->GetDOMObject();
      MOZ_ASSERT(database);
    }

    return database;
  }();

  if (!database || 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 {
    SetResultAndDispatchSuccessEvent(mRequest, nullptr, *database);
  }

  if (database) {
    MOZ_ASSERT(mDatabaseActor == databaseActor);

    databaseActor->ReleaseDOMObject();
  } else {
    databaseActor->SendDeleteMeInternal();
  }
  MOZ_ASSERT(!mDatabaseActor);
}

void BackgroundFactoryRequestChild::HandleResponse(
    const DeleteDatabaseRequestResponse& aResponse) {
  AssertIsOnOwningThread();

  RefPtr<Event> successEvent = IDBVersionChangeEvent::Create(
      mRequest.get(), nsDependentString(kSuccessEventType),
      aResponse.previousVersion());
  MOZ_ASSERT(successEvent);

  SetResultAndDispatchSuccessEvent(mRequest, nullptr, JS::UndefinedHandleValue,
                                   std::move(successEvent));

  MOZ_ASSERT(!mDatabaseActor);
}

void BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
  AssertIsOnOwningThread();

  MaybeCollectGarbageOnIPCMessage();

  if (aWhy != Deletion) {
    GetOpenDBRequest()->NoteComplete();
  }
}

mozilla::ipc::IPCResult BackgroundFactoryRequestChild::Recv__delete__(
    const FactoryRequestResponse& aResponse) {
  AssertIsOnOwningThread();

  MaybeCollectGarbageOnIPCMessage();

  switch (aResponse.type()) {
    case FactoryRequestResponse::Tnsresult:
      HandleResponse(aResponse.get_nsresult());
      break;

    case FactoryRequestResponse::TOpenDatabaseRequestResponse:
      HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
      break;

    case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
      HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
      break;

    default:
      return IPC_FAIL(this"Unknown response type!");
  }

  auto request = GetOpenDBRequest();
  request->NoteComplete();

  return IPC_OK();
}

mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvBlocked(
    const uint64_t aCurrentVersion) {
  AssertIsOnOwningThread();

  MaybeCollectGarbageOnIPCMessage();

  const nsDependentString type(kBlockedEventType);

  RefPtr<Event> blockedEvent;
  if (mIsDeleteOp) {
    blockedEvent =
        IDBVersionChangeEvent::Create(mRequest.get(), type, aCurrentVersion);
    MOZ_ASSERT(blockedEvent);
  } else {
    blockedEvent = IDBVersionChangeEvent::Create(
        mRequest.get(), type, aCurrentVersion, mRequestedVersion);
    MOZ_ASSERT(blockedEvent);
  }

  RefPtr<IDBRequest> kungFuDeathGrip = mRequest;

  IDB_LOG_MARK_CHILD_REQUEST("Firing \"blocked\" event""\"blocked\"",
                             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(MakeUnique<DatabaseSpec>(aSpec)),
      mOpenRequestActor(aOpenRequestActor),
      mDatabase(nullptr),
      mPendingInvalidate(false) {
  // 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<BackgroundFactoryChild*>(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());
  }
}

bool BackgroundDatabaseChild::EnsureDOMObject() {
  AssertIsOnOwningThread();
  MOZ_ASSERT(mOpenRequestActor);

  if (mTemporaryStrongDatabase) {
    MOZ_ASSERT(!mSpec);
    MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase);
    return true;
  }

  MOZ_ASSERT(mSpec);

  const auto request = mOpenRequestActor->GetOpenDBRequest();

  auto& factory =
      static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject();

  if (!factory.GetOwnerGlobal()) {
    // Already disconnected from global.

    // We need to clear mOpenRequestActor here, since that would otherwise be
    // done by ReleaseDOMObject, which cannot be called if EnsureDOMObject
    // failed.
    mOpenRequestActor = nullptr;

    return false;
  }

  // TODO: This AcquireStrongRefFromRawPtr looks suspicious. This should be
  // changed or at least well explained, see also comment on
  // BackgroundFactoryChild.
  mTemporaryStrongDatabase = IDBDatabase::Create(
      request, SafeRefPtr{&factory, AcquireStrongRefFromRawPtr{}}, this,
      std::move(mSpec));

  MOZ_ASSERT(mTemporaryStrongDatabase);
  mTemporaryStrongDatabase->AssertIsOnOwningThread();

  mDatabase = mTemporaryStrongDatabase;

  if (mPendingInvalidate) {
    mDatabase->Invalidate();
    mPendingInvalidate = false;
  }

  mOpenRequestActor->SetDatabaseActor(this);

  return true;
}

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;

  // XXX Why isn't mDatabase set to nullptr here?
}

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) const {
  AssertIsOnOwningThread();
  MOZ_ASSERT(aActor);

  delete aActor;
  return true;
}

already_AddRefed<PBackgroundIDBVersionChangeTransactionChild>
BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild(
    const uint64_t aCurrentVersion, const uint64_t aRequestedVersion,
    const int64_t aNextObjectStoreId, const int64_t aNextIndexId) {
  AssertIsOnOwningThread();

  return RefPtr{new BackgroundVersionChangeTransactionChild(
                    mOpenRequestActor->GetOpenDBRequest())}
      .forget();
}

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(mOpenRequestActor);

  MaybeCollectGarbageOnIPCMessage();

  autoconst actor =
      static_cast<BackgroundVersionChangeTransactionChild*>(aActor);

  if (!EnsureDOMObject()) {
    NS_WARNING("Factory is already disconnected from global");

    actor->SendDeleteMeInternal(true);

    // XXX This is a hack to ensure that transaction/request serial numbers stay
    // in sync between parent and child. Actually, it might be better to create
    // an IDBTransaction in the child and abort that.
    Unused
        << mozilla::ipc::BackgroundChildImpl::GetThreadLocalForCurrentThread()
               ->mIndexedDBThreadLocal->NextTransactionSN(
                   IDBTransaction::Mode::VersionChange);
    Unused << IDBRequest::NextSerialNumber();

    // No reason to IPC_FAIL here.
    return IPC_OK();
  }

  MOZ_ASSERT(!mDatabase->IsInvalidated());

  // XXX NotNull might encapsulate this
  const auto request =
      WrapNotNullUnchecked(RefPtr{mOpenRequestActor->GetOpenDBRequest().get()});

  SafeRefPtr<IDBTransaction> transaction = IDBTransaction::CreateVersionChange(
      mDatabase, actor, request, aNextObjectStoreId, aNextIndexId);
  MOZ_ASSERT(transaction);

  transaction->AssertIsOnOwningThread();

  actor->SetDOMTransaction(transaction.clonePtr());

  const auto database = WrapNotNull(mDatabase);

  database->EnterSetVersionTransaction(aRequestedVersion);

  request->SetTransaction(transaction.clonePtr());

  RefPtr<Event> upgradeNeededEvent = IDBVersionChangeEvent::Create(
      request.get(), nsDependentString(kUpgradeNeededEventType),
      aCurrentVersion, aRequestedVersion);
  MOZ_ASSERT(upgradeNeededEvent);

  SetResultAndDispatchSuccessEvent(
      WrapNotNullUnchecked<RefPtr<IDBRequest>>(request.get()), transaction,
      *database, std::move(upgradeNeededEvent));

  return IPC_OK();
}

mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvVersionChange(
    const uint64_t aOldVersion, const Maybe<uint64_t> aNewVersion) {
  AssertIsOnOwningThread();

  MaybeCollectGarbageOnIPCMessage();

  if (!mDatabase || mDatabase->IsClosed()) {
    return IPC_OK();
  }

  RefPtr<IDBDatabase> kungFuDeathGrip = mDatabase;

  // Handle bfcache'd windows.
  if (nsGlobalWindowInner* owner = kungFuDeathGrip->GetOwnerWindow()) {
    // 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 (owner->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<Event> versionChangeEvent;

  if (aNewVersion.isNothing()) {
    versionChangeEvent =
        IDBVersionChangeEvent::Create(kungFuDeathGrip, type, aOldVersion);
    MOZ_ASSERT(versionChangeEvent);
  } else {
    versionChangeEvent = IDBVersionChangeEvent::Create(
        kungFuDeathGrip, type, aOldVersion, aNewVersion.value());
    MOZ_ASSERT(versionChangeEvent);
  }

  IDB_LOG_MARK("Child : Firing \"versionchange\" event",
               "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();
  } else {
    mPendingInvalidate = true;
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult
BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete() {
  AssertIsOnOwningThread();

  MaybeCollectGarbageOnIPCMessage();

  if (mDatabase) {
    mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType));
  }

  return IPC_OK();
}

/*******************************************************************************
 * BackgroundTransactionBase
 ******************************************************************************/


BackgroundTransactionBase::BackgroundTransactionBase(
    SafeRefPtr<IDBTransaction> aTransaction)
    : mTemporaryStrongTransaction(std::move(aTransaction)),
      mTransaction(mTemporaryStrongTransaction.unsafeGetRawPtr()) {
  MOZ_ASSERT(mTransaction);
  mTransaction->AssertIsOnOwningThread();

  MOZ_COUNT_CTOR(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(
    SafeRefPtr<IDBTransaction> aTransaction) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(aTransaction);
  aTransaction->AssertIsOnOwningThread();
  MOZ_ASSERT(!mTemporaryStrongTransaction);
  MOZ_ASSERT(!mTransaction);

  mTemporaryStrongTransaction = std::move(aTransaction);
  mTransaction = mTemporaryStrongTransaction.unsafeGetRawPtr();
}

void BackgroundTransactionBase::NoteComplete() {
  AssertIsOnOwningThread();
  MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction);

  mTemporaryStrongTransaction = nullptr;
}

/*******************************************************************************
 * BackgroundTransactionChild
 ******************************************************************************/


BackgroundTransactionChild::BackgroundTransactionChild(
    SafeRefPtr<IDBTransaction> aTransaction)
    : BackgroundTransactionBase(std::move(aTransaction)) {
  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();
}

mozilla::ipc::IPCResult BackgroundTransactionChild::RecvComplete(
    const nsresult aResult) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(mTransaction);

  MaybeCollectGarbageOnIPCMessage();

  mTransaction->FireCompleteOrAbortEvents(aResult);

  NoteComplete();
  return IPC_OK();
}

PBackgroundIDBRequestChild*
BackgroundTransactionChild::AllocPBackgroundIDBRequestChild(
    const int64_t& aRequestId, 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 int64_t& aRequestId, const OpenCursorParams& aParams) {
  AssertIsOnOwningThread();

  MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
}

bool BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild(
    PBackgroundIDBCursorChild* aActor) {
  MOZ_ASSERT(aActor);

  delete 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();
}

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();
  }

  RefPtr<IDBOpenDBRequest> request = mOpenDBRequest;
  MOZ_ASSERT(request);

  mTransaction->FireCompleteOrAbortEvents(aResult);

  request->SetTransaction(nullptr);
  request = nullptr;

  mOpenDBRequest = nullptr;

  NoteComplete();
  return IPC_OK();
}

PBackgroundIDBRequestChild*
BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild(
    const int64_t& aRequestId, 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 int64_t& aRequestId, const OpenCursorParams& aParams) {
  AssertIsOnOwningThread();

  MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
}

bool BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild(
    PBackgroundIDBCursorChild* aActor) {
  MOZ_ASSERT(aActor);

  delete aActor;
  return true;
}

/*******************************************************************************
 * BackgroundRequestChild
 ******************************************************************************/


BackgroundRequestChild::BackgroundRequestChild(
    MovingNotNull<RefPtr<IDBRequest>> aRequest)
    : BackgroundRequestChildBase(std::move(aRequest)),
      mTransaction(mRequest->AcquireTransaction()),
      mRunningPreprocessHelpers(0),
      mCurrentCloneDataIndex(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 aCloneDataIndex, UniquePtr<JSStructuredCloneData> aCloneData) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(aCloneDataIndex < mCloneInfos.Length());
  MOZ_ASSERT(aCloneData);

  auto& cloneInfo = mCloneInfos[aCloneDataIndex];
  MOZ_ASSERT(cloneInfo.mPreprocessHelper);
  MOZ_ASSERT(!cloneInfo.mCloneData);

  cloneInfo.mCloneData = std::move(aCloneData);

  MaybeSendContinue();

  cloneInfo.mPreprocessHelper = nullptr;
}

void BackgroundRequestChild::OnPreprocessFailed(uint32_t aCloneDataIndex,
                                                nsresult aErrorCode) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(aCloneDataIndex < mCloneInfos.Length());
  MOZ_ASSERT(NS_FAILED(aErrorCode));

  auto& cloneInfo = mCloneInfos[aCloneDataIndex];
  MOZ_ASSERT(cloneInfo.mPreprocessHelper);
  MOZ_ASSERT(!cloneInfo.mCloneData);

  if (NS_SUCCEEDED(mPreprocessResultCode)) {
    mPreprocessResultCode = aErrorCode;
  }

  MaybeSendContinue();

  cloneInfo.mPreprocessHelper = nullptr;
}

UniquePtr<JSStructuredCloneData> BackgroundRequestChild::GetNextCloneData() {
  AssertIsOnOwningThread();
  MOZ_ASSERT(mCurrentCloneDataIndex < mCloneInfos.Length());
  MOZ_ASSERT(mCloneInfos[mCurrentCloneDataIndex].mCloneData);

  return std::move(mCloneInfos[mCurrentCloneDataIndex++].mCloneData);
}

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.clonePtr());
}

void BackgroundRequestChild::HandleResponse(const Key& aResponse) {
  AssertIsOnOwningThread();

  SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse);
}

void BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse) {
  AssertIsOnOwningThread();

  SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse);
}

void BackgroundRequestChild::HandleResponse(
    SerializedStructuredCloneReadInfo&& aResponse) {
  AssertIsOnOwningThread();

  if (!mTransaction->Database()->GetOwnerGlobal()) {
    // Ignore the response, since we have already been disconnected from the
    // global.
    return;
  }

  auto cloneReadInfo = DeserializeStructuredCloneReadInfo(
      std::move(aResponse), mTransaction->Database(),
      [this] { return std::move(*GetNextCloneData()); });

  SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(),
                                   cloneReadInfo);
}

void BackgroundRequestChild::HandleResponse(
    nsTArray<SerializedStructuredCloneReadInfo>&& aResponse) {
  AssertIsOnOwningThread();

  if (!mTransaction->Database()->GetOwnerGlobal()) {
    // Ignore the response, since we have already been disconnected from the
    // global.
    return;
  }

  nsTArray<StructuredCloneReadInfoChild> cloneReadInfos;

  QM_TRY(OkIf(cloneReadInfos.SetCapacity(aResponse.Length(), fallible)),
         QM_VOID, ([&aResponse, this](const auto) {
           // Since we are under memory pressure, release aResponse early.
           aResponse.Clear();

           DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
                              AcquireTransaction());

           MOZ_ASSERT(mTransaction->IsAborted());
         }));

  std::transform(std::make_move_iterator(aResponse.begin()),
                 std::make_move_iterator(aResponse.end()),
                 MakeBackInserter(cloneReadInfos),
                 [database = mTransaction->Database(), this](
                     SerializedStructuredCloneReadInfo&& serializedCloneInfo) {
                   return DeserializeStructuredCloneReadInfo(
                       std::move(serializedCloneInfo), database,
                       [this] { return std::move(*GetNextCloneData()); });
                 });

  SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(),
                                   cloneReadInfos);
}

void BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse) {
  AssertIsOnOwningThread();

  SetResultAndDispatchSuccessEvent(
      mRequest, AcquireTransaction(),
      const_cast<const JS::Handle<JS::Value>&>(aResponse));
}

void BackgroundRequestChild::HandleResponse(const uint64_t aResponse) {
  AssertIsOnOwningThread();

  SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse);
}

nsresult BackgroundRequestChild::HandlePreprocess(
    const PreprocessInfo& aPreprocessInfo) {
  return HandlePreprocessInternal(
      AutoTArray<PreprocessInfo, 1>{aPreprocessInfo});
}

nsresult BackgroundRequestChild::HandlePreprocess(
    const nsTArray<PreprocessInfo>& aPreprocessInfos) {
  AssertIsOnOwningThread();
  mGetAll = true;

  return HandlePreprocessInternal(aPreprocessInfos);
}

nsresult BackgroundRequestChild::HandlePreprocessInternal(
    const nsTArray<PreprocessInfo>& aPreprocessInfos) {
  AssertIsOnOwningThread();

  IDBDatabase* database = mTransaction->Database();

  const uint32_t count = aPreprocessInfos.Length();

  mCloneInfos.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 PreprocessInfo& preprocessInfo = aPreprocessInfos[index];

    const auto files =
        DeserializeStructuredCloneFiles(database, preprocessInfo.files(),
                                        /* aForPreprocess */ true);

    MOZ_ASSERT(files.Length() == 1);

    auto& preprocessHelper = mCloneInfos[index].mPreprocessHelper;
    preprocessHelper = MakeRefPtr<PreprocessHelper>(index, this);

    nsresult rv = preprocessHelper->Init(files[0]);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    rv = preprocessHelper->Dispatch();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    mRunningPreprocessHelpers++;
  }

  return NS_OK;
}

void BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
  AssertIsOnOwningThread();

  MaybeCollectGarbageOnIPCMessage();

  for (auto& cloneInfo : mCloneInfos) {
    const auto& preprocessHelper = cloneInfo.mPreprocessHelper;

    if (preprocessHelper) {
      preprocessHelper->ClearActor();
    }
  }
  mCloneInfos.Clear();

  if (mTransaction) {
    mTransaction->AssertIsOnOwningThread();

    mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */
                                    aWhy == Deletion);
#ifdef DEBUG
    mTransaction = nullptr;
#endif
  }
}

mozilla::ipc::IPCResult BackgroundRequestChild::Recv__delete__(
    RequestResponse&& aResponse) {
  AssertIsOnOwningThread();
  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(
            std::move(aResponse.get_ObjectStoreGetResponse().cloneInfo()));
        break;

      case RequestResponse::TObjectStoreGetKeyResponse:
        HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key());
        break;

      case RequestResponse::TObjectStoreGetAllResponse:
        HandleResponse(
            std::move(aResponse.get_ObjectStoreGetAllResponse().cloneInfos()));
        break;

      case RequestResponse::TObjectStoreGetAllKeysResponse:
        HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys());
        break;

      case RequestResponse::TObjectStoreDeleteResponse:
      case RequestResponse::TObjectStoreClearResponse:
        HandleResponse(JS::UndefinedHandleValue);
        break;

      case RequestResponse::TObjectStoreCountResponse:
        HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
        break;

      case RequestResponse::TIndexGetResponse:
        HandleResponse(std::move(aResponse.get_IndexGetResponse().cloneInfo()));
        break;

      case RequestResponse::TIndexGetKeyResponse:
        HandleResponse(aResponse.get_IndexGetKeyResponse().key());
        break;

      case RequestResponse::TIndexGetAllResponse:
        HandleResponse(
            std::move(aResponse.get_IndexGetAllResponse().cloneInfos()));
        break;

      case RequestResponse::TIndexGetAllKeysResponse:
        HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
        break;

      case RequestResponse::TIndexCountResponse:
        HandleResponse(aResponse.get_IndexCountResponse().count());
        break;

      default:
        return IPC_FAIL(this"Unknown response type!");
    }
  }

  mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ 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: {
      const auto& params = aParams.get_ObjectStoreGetPreprocessParams();

      rv = HandlePreprocess(params.preprocessInfo());

      break;
    }

    case PreprocessParams::TObjectStoreGetAllPreprocessParams: {
      const auto& params = aParams.get_ObjectStoreGetAllPreprocessParams();

      rv = HandlePreprocess(params.preprocessInfos());

      break;
    }

    default:
      return IPC_FAIL(this"Unknown params type!");
  }

  if (NS_WARN_IF(NS_FAILED(rv))) {
    QM_WARNONLY_TRY(OkIf(SendContinue(rv)));
  }

  return IPC_OK();
}

nsresult BackgroundRequestChild::PreprocessHelper::Init(
    const StructuredCloneFileChild& aFile) {
  AssertIsOnOwningThread();
  MOZ_ASSERT(aFile.HasBlob());
  MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eStructuredClone);
  MOZ_ASSERT(mState == State::Initial);

  // 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);

  // 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 = TaskQueue::Create(target.forget(), "BackgroundRequestChild");

  ErrorResult errorResult;

  nsCOMPtr<nsIInputStream> stream;
  // XXX After Bug 1620560, MutableBlob is not needed here anymore.
  aFile.MutableBlob().CreateInputStream(getter_AddRefs(stream), errorResult);
  if (NS_WARN_IF(errorResult.Failed())) {
    return errorResult.StealNSResult();
  }

  mStream = std::move(stream);

  mCloneData = MakeUnique<JSStructuredCloneData>(
      JS::StructuredCloneScope::DifferentProcessForIndexedDB);

  return NS_OK;
}

nsresult BackgroundRequestChild::PreprocessHelper::Dispatch() {
  AssertIsOnOwningThread();
  MOZ_ASSERT(mState == State::Initial);

  nsresult rv = mTaskQueue->Dispatch(this, NS_DISPATCH_NORMAL);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return NS_OK;
}

nsresult BackgroundRequestChild::PreprocessHelper::Start() {
  MOZ_ASSERT(!IsOnOwningThread());
  MOZ_ASSERT(mStream);
  MOZ_ASSERT(mState == State::Initial);

  nsresult rv;

  PRFileDesc* fileDesc = GetFileDescriptorFromStream(mStream);
  if (fileDesc) {
    rv = ProcessStream();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    return NS_OK;
  }

  mState = State::WaitingForStreamReady;

  nsCOMPtr<nsIAsyncFileMetadata> asyncFileMetadata = do_QueryInterface(mStream);
  if (asyncFileMetadata) {
    rv = asyncFileMetadata->AsyncFileMetadataWait(this, mTaskQueue);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    return NS_OK;
  }

  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
  if (!asyncStream) {
    return NS_ERROR_NO_INTERFACE;
  }

  rv = asyncStream->AsyncWait(this, 0, 0, mTaskQueue);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return NS_OK;
}

nsresult BackgroundRequestChild::PreprocessHelper::ProcessStream() {
  MOZ_ASSERT(!IsOnOwningThread());
  MOZ_ASSERT(mStream);
  MOZ_ASSERT(mState == State::Initial ||
             mState == State::WaitingForStreamReady);

  // We need to get the internal stream (which is an nsFileInputStream) because
  // SnappyUncompressInputStream doesn't support reading from async input
  // streams.

  nsCOMPtr<mozIRemoteLazyInputStream> blobInputStream =
      do_QueryInterface(mStream);
  MOZ_ASSERT(blobInputStream);

  nsCOMPtr<nsIInputStream> internalInputStream;
  MOZ_ALWAYS_SUCCEEDS(
      blobInputStream->TakeInternalStream(getter_AddRefs(internalInputStream)));
  MOZ_ASSERT(internalInputStream);

  QM_TRY(MOZ_TO_RESULT(
      SnappyUncompressStructuredCloneData(*internalInputStream, *mCloneData)));

  mState = State::Finishing;

  QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)));

  return NS_OK;
}

void BackgroundRequestChild::PreprocessHelper::Finish() {
  AssertIsOnOwningThread();

  if (mActor) {
    if (NS_SUCCEEDED(mResultCode)) {
      mActor->OnPreprocessFinished(mCloneDataIndex, std::move(mCloneData));

      MOZ_ASSERT(!mCloneData);
    } else {
      mActor->OnPreprocessFailed(mCloneDataIndex, mResultCode);
    }
  }

  mState = State::Completed;
}

NS_IMPL_ISUPPORTS_INHERITED(BackgroundRequestChild::PreprocessHelper,
                            DiscardableRunnable, nsIInputStreamCallback,
                            nsIFileMetadataCallback)

NS_IMETHODIMP
BackgroundRequestChild::PreprocessHelper::Run() {
  nsresult rv;

  switch (mState) {
    case State::Initial:
      rv = Start();
      break;

    case State::WaitingForStreamReady:
      rv = ProcessStream();
      break;

    case State::Finishing:
      Finish();
      return NS_OK;

    default:
      MOZ_CRASH("Bad state!");
  }

  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
    if (NS_SUCCEEDED(mResultCode)) {
      mResultCode = rv;
    }

    // Must set mState before dispatching otherwise we will race with the owning
    // thread.
    mState = State::Finishing;

    if (IsOnOwningThread()) {
      Finish();
    } else {
      MOZ_ALWAYS_SUCCEEDS(
          mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
    }
  }

  return NS_OK;
}

NS_IMETHODIMP
BackgroundRequestChild::PreprocessHelper::OnInputStreamReady(
    nsIAsyncInputStream* aStream) {
  MOZ_ASSERT(!IsOnOwningThread());
  MOZ_ASSERT(mState == State::WaitingForStreamReady);

  MOZ_ALWAYS_SUCCEEDS(this->Run());

  return NS_OK;
}

NS_IMETHODIMP
BackgroundRequestChild::PreprocessHelper::OnFileMetadataReady(
    nsIAsyncFileMetadata* aObject) {
  MOZ_ASSERT(!IsOnOwningThread());
  MOZ_ASSERT(mState == State::WaitingForStreamReady);

  MOZ_ALWAYS_SUCCEEDS(this->Run());

  return NS_OK;
}

/*******************************************************************************
 * BackgroundCursorChild
 ******************************************************************************/


BackgroundCursorChildBase::BackgroundCursorChildBase(
    const NotNull<IDBRequest*> aRequest, const Direction aDirection)
    : mRequest(aRequest),
      mTransaction(aRequest->MaybeTransactionRef()),
      mStrongRequest(aRequest),
      mDirection(aDirection) {
  MOZ_ASSERT(mTransaction);
}

MovingNotNull<RefPtr<IDBRequest>> BackgroundCursorChildBase::AcquireRequest()
    const {
  AssertIsOnOwningThread();

  // XXX This could be encapsulated by NotNull
  return WrapNotNullUnchecked(RefPtr{mRequest->get()});
}

template <IDBCursorType CursorType>
BackgroundCursorChild<CursorType>::BackgroundCursorChild(
    const NotNull<IDBRequest*> aRequest, SourceType* aSource,
    Direction aDirection)
    : BackgroundCursorChildBase(aRequest, aDirection),
      mSource(WrapNotNull(aSource)),
      mCursor(nullptr),
      mInFlightResponseInvalidationNeeded(false) {
  aSource->AssertIsOnOwningThread();

  MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild<CursorType>);
}

template <IDBCursorType CursorType>
BackgroundCursorChild<CursorType>::~BackgroundCursorChild() {
  MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild<CursorType>);
}

template <IDBCursorType CursorType>
SafeRefPtr<BackgroundCursorChild<CursorType>>
BackgroundCursorChild<CursorType>::SafeRefPtrFromThis() {
  return BackgroundCursorChildBase::SafeRefPtrFromThis()
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=99 G=96

¤ Dauer der Verarbeitung: 0.53 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.