dom/workers/remoteworkers/RemoteWorkerChild.cpp
author Andrea Marchesini <amarchesini@mozilla.com>
Mon, 19 Nov 2018 15:18:33 -0800
changeset 503571 b3d9a379c852669a1fa005148bee537b99c91c6f
parent 503570 6059af9c85adc62a51d9b38b9cdcba3904d4d824
child 503579 7733e4286f9c615bde46ad140329ab73dd613e1e
permissions -rw-r--r--
Bug 1438945 - Part 9: RemoteWorker in SharedWorkerManager. r=asuth

/* -*- 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 "RemoteWorkerChild.h"
#include "RemoteWorkerService.h"
#include "mozilla/dom/IndexedDatabaseManager.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/RemoteWorkerTypes.h"
#include "mozilla/dom/ServiceWorkerInterceptController.h"
#include "mozilla/dom/workerinternals/ScriptLoader.h"
#include "mozilla/dom/WorkerError.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsIConsoleReportCollector.h"
#include "nsIPrincipal.h"
#include "nsNetUtil.h"
#include "nsProxyRelease.h"

namespace mozilla {

using namespace ipc;

namespace dom {

using workerinternals::ChannelFromScriptURLMainThread;

namespace {

nsresult
PopulateContentSecurityPolicy(nsIContentSecurityPolicy* aCSP,
                              const nsTArray<ContentSecurityPolicy>& aPolicies)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aCSP);
  MOZ_ASSERT(!aPolicies.IsEmpty());

  for (const ContentSecurityPolicy& policy : aPolicies) {
    nsresult rv = aCSP->AppendPolicy(policy.policy(),
                                     policy.reportOnlyFlag(),
                                     policy.deliveredViaMetaTagFlag());
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  return NS_OK;
}

nsresult
PopulatePrincipalContentSecurityPolicy(nsIPrincipal* aPrincipal,
                                       const nsTArray<ContentSecurityPolicy>& aPolicies,
                                       const nsTArray<ContentSecurityPolicy>& aPreloadPolicies)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPrincipal);

  if (!aPolicies.IsEmpty()) {
    nsCOMPtr<nsIContentSecurityPolicy> csp;
    aPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
    nsresult rv = PopulateContentSecurityPolicy(csp, aPolicies);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  if (!aPreloadPolicies.IsEmpty()) {
    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
    aPrincipal->EnsurePreloadCSP(nullptr, getter_AddRefs(preloadCsp));
    nsresult rv = PopulateContentSecurityPolicy(preloadCsp, aPreloadPolicies);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  return NS_OK;
}

class SharedWorkerInterfaceRequestor final : public nsIInterfaceRequestor
{
public:
  NS_DECL_ISUPPORTS

  SharedWorkerInterfaceRequestor()
    : mSWController(new ServiceWorkerInterceptController())
  {}

  NS_IMETHOD
  GetInterface(const nsIID& aIID, void** aSink) override
  {
    MOZ_ASSERT(NS_IsMainThread());

    if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController))) {
      // If asked for the network intercept controller, ask the outer requestor,
      // which could be the docshell.
      RefPtr<ServiceWorkerInterceptController> swController = mSWController;
      swController.forget(aSink);
      return NS_OK;
    }

    return NS_NOINTERFACE;
  }

private:
  ~SharedWorkerInterfaceRequestor() = default;

  RefPtr<ServiceWorkerInterceptController> mSWController;
};

NS_IMPL_ADDREF(SharedWorkerInterfaceRequestor)
NS_IMPL_RELEASE(SharedWorkerInterfaceRequestor)
NS_IMPL_QUERY_INTERFACE(SharedWorkerInterfaceRequestor, nsIInterfaceRequestor)

// Normal runnable because AddPortIdentifier() is going to exec JS code.
class MessagePortIdentifierRunnable final : public WorkerRunnable
{
public:
  MessagePortIdentifierRunnable(WorkerPrivate* aWorkerPrivate,
                                RemoteWorkerChild* aActor,
                                const MessagePortIdentifier& aPortIdentifier)
    : WorkerRunnable(aWorkerPrivate)
    , mActor(aActor)
    , mPortIdentifier(aPortIdentifier)
  {}

private:
  virtual bool
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
  {
    mActor->AddPortIdentifier(aCx, aWorkerPrivate, mPortIdentifier);
    return true;
  }

  nsresult
  Cancel() override
  {
    MessagePort::ForceClose(mPortIdentifier);
    return WorkerRunnable::Cancel();
  }

  virtual bool
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
  {
    // Silence bad assertions.
    return true;
  }

  virtual void
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
  {
    // Silence bad assertions.
  }

  bool
  PreRun(WorkerPrivate* aWorkerPrivate) override
  {
    // Silence bad assertions.
    return true;
  }

  void
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
          bool aRunResult) override
  {
    // Silence bad assertions.
    return;
  }


  RefPtr<RemoteWorkerChild> mActor;
  MessagePortIdentifier mPortIdentifier;
};

} // anonymous

class RemoteWorkerChild::InitializeWorkerRunnable final : public WorkerRunnable
{
public:
  InitializeWorkerRunnable(WorkerPrivate* aWorkerPrivate,
                           RemoteWorkerChild* aActor)
    : WorkerRunnable(aWorkerPrivate)
    , mActor(aActor)
  {}

private:
  virtual bool
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
  {
    mActor->InitializeOnWorker(aWorkerPrivate);
    return true;
  }

  nsresult
  Cancel() override
  {
    mActor->CreationFailedOnAnyThread();
    mActor->ShutdownOnWorker();
    return WorkerRunnable::Cancel();
  }

  RefPtr<RemoteWorkerChild> mActor;
};

RemoteWorkerChild::RemoteWorkerChild()
  : mIPCActive(true)
  , mWorkerState(ePending)
{
  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
}

RemoteWorkerChild::~RemoteWorkerChild()
{
  nsCOMPtr<nsIEventTarget> target =
    SystemGroup::EventTargetFor(TaskCategory::Other);

  NS_ProxyRelease("RemoteWorkerChild::mWorkerPrivate",
                  target, mWorkerPrivate.forget());
}

void
RemoteWorkerChild::ActorDestroy(ActorDestroyReason aWhy)
{
  mIPCActive = false;
  mPendingOps.Clear();
}

void
RemoteWorkerChild::ExecWorker(const RemoteWorkerData& aData)
{
  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
  MOZ_ASSERT(mIPCActive);

  RefPtr<RemoteWorkerChild> self = this;
  nsCOMPtr<nsIRunnable> r =
    NS_NewRunnableFunction("RemoteWorkerChild::ExecWorker",
                           [self, aData]() {
      nsresult rv = self->ExecWorkerOnMainThread(aData);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        self->CreationFailedOnAnyThread();
      }
  });

  nsCOMPtr<nsIEventTarget> target =
    SystemGroup::EventTargetFor(TaskCategory::Other);
  target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

nsresult
RemoteWorkerChild::ExecWorkerOnMainThread(const RemoteWorkerData& aData)
{
  MOZ_ASSERT(NS_IsMainThread());

  // Ensure that the IndexedDatabaseManager is initialized
  Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());

  nsresult rv = NS_OK;

  nsCOMPtr<nsIPrincipal> principal =
    PrincipalInfoToPrincipal(aData.principalInfo(), &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = PopulatePrincipalContentSecurityPolicy(principal,
                                              aData.principalCsp(),
                                              aData.principalPreloadCsp());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsCOMPtr<nsIPrincipal> loadingPrincipal =
    PrincipalInfoToPrincipal(aData.loadingPrincipalInfo(), &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = PopulatePrincipalContentSecurityPolicy(loadingPrincipal,
                                              aData.loadingPrincipalCsp(),
                                              aData.loadingPrincipalPreloadCsp());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  WorkerLoadInfo info;
  rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aData.baseScriptURL(),
                 nullptr, nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  rv = NS_NewURI(getter_AddRefs(info.mResolvedScriptURI),
                 aData.resolvedScriptURL(), nullptr, info.mBaseURI);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  info.mPrincipalInfo = new PrincipalInfo(aData.principalInfo());

  info.mDomain = aData.domain();
  info.mPrincipal = principal;
  info.mLoadingPrincipal = loadingPrincipal;

  nsContentUtils::StorageAccess access =
    nsContentUtils::StorageAllowedForPrincipal(info.mPrincipal);
  info.mStorageAllowed =
    access > nsContentUtils::StorageAccess::ePrivateBrowsing;
  info.mOriginAttributes =
    BasePrincipal::Cast(principal)->OriginAttributesRef();

  // Default CSP permissions for now.  These will be overrided if necessary
  // based on the script CSP headers during load in ScriptLoader.
  info.mEvalAllowed = true;
  info.mReportCSPViolations = false;
  info.mSecureContext = aData.isSecureContext()
    ? WorkerLoadInfo::eSecureContext : WorkerLoadInfo::eInsecureContext;

  WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mLoadingPrincipal);

  RefPtr<SharedWorkerInterfaceRequestor> requestor =
    new SharedWorkerInterfaceRequestor();
  info.mInterfaceRequestor->SetOuterRequestor(requestor);

  rv = info.SetPrincipalOnMainThread(info.mPrincipal, info.mLoadGroup);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  Maybe<ClientInfo> clientInfo;
  if (aData.clientInfo().type() == OptionalIPCClientInfo::TIPCClientInfo) {
    clientInfo.emplace(ClientInfo(aData.clientInfo().get_IPCClientInfo()));
  }

  // Top level workers' main script use the document charset for the script
  // uri encoding.
  rv = ChannelFromScriptURLMainThread(info.mLoadingPrincipal,
                                      info.mBaseURI,
                                      nullptr /* parent document */,
                                      info.mLoadGroup,
                                      aData.originalScriptURL(),
                                      clientInfo,
                                      aData.isSharedWorker()
                                        ? nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER
                                        : nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER,
                                      false /* default encoding */,
                                      getter_AddRefs(info.mChannel));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  AutoJSAPI jsapi;
  jsapi.Init();

  ErrorResult error;
  mWorkerPrivate = WorkerPrivate::Constructor(jsapi.cx(),
                                              aData.originalScriptURL(),
                                              false,
                                              aData.isSharedWorker()
                                                ? WorkerTypeShared
                                                : WorkerTypeService,
                                              aData.name(),
                                              VoidCString(),
                                              &info, error);
  if (NS_WARN_IF(error.Failed())) {
    return error.StealNSResult();
  }

  RefPtr<InitializeWorkerRunnable> runnable =
    new InitializeWorkerRunnable(mWorkerPrivate, this);
  if (NS_WARN_IF(!runnable->Dispatch())) {
    return NS_ERROR_FAILURE;
  }

  mWorkerPrivate->SetRemoteWorkerController(this);
  return NS_OK;
}

void
RemoteWorkerChild::InitializeOnWorker(WorkerPrivate* aWorkerPrivate)
{
  MOZ_ASSERT(aWorkerPrivate);
  aWorkerPrivate->AssertIsOnWorkerThread();

  RefPtr<RemoteWorkerChild> self = this;
  mWorkerRef = WeakWorkerRef::Create(mWorkerPrivate, [self]() {
      self->ShutdownOnWorker();
    });

  if (NS_WARN_IF(!mWorkerRef)) {
    CreationFailedOnAnyThread();
    ShutdownOnWorker();
    return;
  }

  CreationSucceededOnAnyThread();
}

void
RemoteWorkerChild::ShutdownOnWorker()
{
  MOZ_ASSERT(mWorkerPrivate);
  mWorkerPrivate->AssertIsOnWorkerThread();

  // This will release the worker.
  mWorkerRef = nullptr;

  nsCOMPtr<nsIEventTarget> target =
    SystemGroup::EventTargetFor(TaskCategory::Other);

  NS_ProxyRelease("RemoteWorkerChild::mWorkerPrivate",
                  target, mWorkerPrivate.forget());

  RefPtr<RemoteWorkerChild> self = this;
  nsCOMPtr<nsIRunnable> r =
    NS_NewRunnableFunction("RemoteWorkerChild::ShutdownOnWorker",
                           [self]() {
      self->WorkerTerminated();
  });

  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

void
RemoteWorkerChild::WorkerTerminated()
{
  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());

  mWorkerState = eTerminated;
  mPendingOps.Clear();

  if (!mIPCActive) {
    return;
  }

  Unused << SendClose();
  mIPCActive = false;
}

void
RemoteWorkerChild::ErrorPropagationDispatch(nsresult aError)
{
  MOZ_ASSERT(NS_FAILED(aError));

  RefPtr<RemoteWorkerChild> self = this;
  nsCOMPtr<nsIRunnable> r =
    NS_NewRunnableFunction("RemoteWorkerChild::ErrorPropagationDispatch",
                           [self, aError]() {
      self->ErrorPropagation(aError);
  });

  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

void
RemoteWorkerChild::ErrorPropagationOnMainThread(const WorkerErrorReport* aReport,
                                                bool aIsErrorEvent)
{
  MOZ_ASSERT(NS_IsMainThread());

  ErrorValue value;
  if (aIsErrorEvent) {
    nsTArray<ErrorDataNote> notes;
    for (size_t i = 0, len = aReport->mNotes.Length(); i < len; i++) {
      const WorkerErrorNote& note = aReport->mNotes.ElementAt(i);
      notes.AppendElement(ErrorDataNote(note.mLineNumber, note.mColumnNumber,
                                        note.mMessage, note.mFilename));
    }

    ErrorData data(aReport->mLineNumber,
                   aReport->mColumnNumber,
                   aReport->mFlags,
                   aReport->mMessage,
                   aReport->mFilename,
                   aReport->mLine,
                   notes);
    value = data;
  } else {
    value = void_t();
  }

  RefPtr<RemoteWorkerChild> self = this;
  nsCOMPtr<nsIRunnable> r =
    NS_NewRunnableFunction("RemoteWorkerChild::ErrorPropagationOnMainThread",
                           [self, value]() {
    self->ErrorPropagation(value);
  });

  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

void
RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue)
{
  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());

  if (!mIPCActive) {
    return;
  }

  Unused << SendError(aValue);
}

void
RemoteWorkerChild::CloseWorkerOnMainThread()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (mWorkerState == ePending) {
    mWorkerState = ePendingTerminated;
    // Already released.
    return;
  }

  // The holder will be notified by this.
  if (mWorkerState == eRunning) {
    MOZ_ASSERT(mWorkerPrivate);
    mWorkerPrivate->Cancel();
  }
}

void
RemoteWorkerChild::FlushReportsOnMainThread(nsIConsoleReportCollector* aReporter)
{
  MOZ_ASSERT(NS_IsMainThread());

  bool reportErrorToBrowserConsole = true;

  // Flush the reports.
  for (uint32_t i = 0, len = mWindowIDs.Length(); i < len; ++i) {
    aReporter->FlushReportsToConsole(mWindowIDs[i],
      nsIConsoleReportCollector::ReportAction::Save);
    reportErrorToBrowserConsole = false;
  }

  // Finally report to browser console if there is no any window.
  if (reportErrorToBrowserConsole) {
    aReporter->FlushReportsToConsole(0);
    return;
  }

  aReporter->ClearConsoleReports();
}

IPCResult
RemoteWorkerChild::RecvExecOp(const RemoteWorkerOp& aOp)
{
  if (!mIPCActive) {
    return IPC_OK();
  }

  // The worker is not ready yet.
  if (mWorkerState == ePending) {
    mPendingOps.AppendElement(aOp);
    return IPC_OK();
  }

  if (mWorkerState == eTerminated || mWorkerState == ePendingTerminated) {
    // No op.
    return IPC_OK();
  }

  MOZ_ASSERT(mWorkerState == eRunning);

  // Main-thread operations
  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp ||
      aOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp ||
      aOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp ||
      aOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp ||
      aOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp ||
      aOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp ||
      aOp.type() == RemoteWorkerOp::TRemoteWorkerRemoveWindowIDOp) {
    RefPtr<RemoteWorkerChild> self = this;
    nsCOMPtr<nsIRunnable> r =
      NS_NewRunnableFunction("RemoteWorkerChild::RecvExecOp",
                             [self, aOp]() {
        self->RecvExecOpOnMainThread(aOp);
    });

    nsCOMPtr<nsIEventTarget> target =
      SystemGroup::EventTargetFor(TaskCategory::Other);
    target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    return IPC_OK();
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
    const RemoteWorkerPortIdentifierOp& op =
      aOp.get_RemoteWorkerPortIdentifierOp();
    RefPtr<MessagePortIdentifierRunnable> runnable =
      new MessagePortIdentifierRunnable(mWorkerPrivate, this,
                                        op.portIdentifier());
    if (NS_WARN_IF(!runnable->Dispatch())) {
      ErrorPropagation(NS_ERROR_FAILURE);
    }
    return IPC_OK();
  }

  MOZ_CRASH("Unknown operation.");

  return IPC_OK();
}

void
RemoteWorkerChild::RecvExecOpOnMainThread(const RemoteWorkerOp& aOp)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp) {
    if (mWorkerPrivate) {
      mWorkerPrivate->ParentWindowPaused();
    }
    return;
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp) {
    if (mWorkerPrivate) {
      mWorkerPrivate->ParentWindowResumed();
    }
    return;
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp) {
    if (mWorkerPrivate) {
      mWorkerPrivate->Freeze(nullptr);
    }
    return;
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp) {
    if (mWorkerPrivate) {
      mWorkerPrivate->Thaw(nullptr);
    }
    return;
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp) {
    CloseWorkerOnMainThread();
    return;
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp) {
    mWindowIDs.AppendElement(aOp.get_RemoteWorkerAddWindowIDOp().windowID());
    return;
  }

  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerRemoveWindowIDOp) {
    mWindowIDs.RemoveElement(aOp.get_RemoteWorkerRemoveWindowIDOp().windowID());
    return;
  }

  MOZ_CRASH("No other operations should be scheduled on main-thread.");
}

void
RemoteWorkerChild::AddPortIdentifier(JSContext* aCx,
                                     WorkerPrivate* aWorkerPrivate,
                                     const MessagePortIdentifier& aPortIdentifier)
{
  if (NS_WARN_IF(!aWorkerPrivate->ConnectMessagePort(aCx, aPortIdentifier))) {
    ErrorPropagationDispatch(NS_ERROR_FAILURE);
  }
}

void
RemoteWorkerChild::CreationSucceededOnAnyThread()
{
  RefPtr<RemoteWorkerChild> self = this;
  nsCOMPtr<nsIRunnable> r =
    NS_NewRunnableFunction("RemoteWorkerChild::CreationSucceededOnAnyThread",
                           [self]() {
    self->CreationSucceeded();
  });

  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

void
RemoteWorkerChild::CreationSucceeded()
{
  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());

  // The worker is created but we need to terminate it already.
  if (mWorkerState == ePendingTerminated) {
    RefPtr<RemoteWorkerChild> self = this;
    nsCOMPtr<nsIRunnable> r =
      NS_NewRunnableFunction("RemoteWorkerChild::CreationSucceeded",
                             [self]() {
        self->CloseWorkerOnMainThread();
    });

    nsCOMPtr<nsIEventTarget> target =
      SystemGroup::EventTargetFor(TaskCategory::Other);
    target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    return;
  }

  mWorkerState = eRunning;

  if (!mIPCActive) {
    return;
  }

  for (const RemoteWorkerOp& op : mPendingOps) {
    RecvExecOp(op);
  }

  mPendingOps.Clear();

  Unused << SendCreated(true);
}

void
RemoteWorkerChild::CreationFailedOnAnyThread()
{
  RefPtr<RemoteWorkerChild> self = this;
  nsCOMPtr<nsIRunnable> r =
    NS_NewRunnableFunction("RemoteWorkerChild::CreationFailedOnAnyThread",
                           [self]() {
    self->CreationFailed();
  });

  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
}

void
RemoteWorkerChild::CreationFailed()
{
  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());

  mWorkerState = eTerminated;
  mPendingOps.Clear();

  if (!mIPCActive) {
    return;
  }

  Unused << SendCreated(false);
}

} // dom namespace
} // mozilla namespace