Bug 1231213 - Refactor RemoteWorkerChild to handle Service Workers' and Shared Workers' operations and to make its state data/transitions safer. r=asuth
☠☠ backed out by 3cf55b7f12f2 ☠ ☠
authorPerry Jiang <perry@mozilla.com>
Wed, 14 Aug 2019 16:20:06 +0000
changeset 487976 666bf42600469276c778a9ab9a4e1324eccd02ea
parent 487975 0b03a19a6dc141f49ead7555b200e5b7498124c0
child 487977 85df1959eb98a95bf7aa59332d366ebb3f45d1eb
push id36434
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:44:30 +0000
treeherdermozilla-central@144fbfb409b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1231213
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1231213 - Refactor RemoteWorkerChild to handle Service Workers' and Shared Workers' operations and to make its state data/transitions safer. r=asuth Differential Revision: https://phabricator.services.mozilla.com/D26170
dom/workers/remoteworkers/RemoteWorkerChild.cpp
dom/workers/remoteworkers/RemoteWorkerChild.h
dom/workers/remoteworkers/RemoteWorkerParent.cpp
dom/workers/remoteworkers/RemoteWorkerTypes.ipdlh
ipc/glue/BackgroundChildImpl.cpp
--- a/dom/workers/remoteworkers/RemoteWorkerChild.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerChild.cpp
@@ -1,42 +1,101 @@
 /* -*- 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 <utility>
+
+#include "MainThreadUtils.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsIConsoleReportCollector.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIPrincipal.h"
+#include "nsIPermissionManager.h"
+#include "nsNetUtil.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
 #include "RemoteWorkerService.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Services.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/RemoteWorkerTypes.h"
+#include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/dom/ServiceWorkerInterceptController.h"
+#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 #include "mozilla/dom/ServiceWorkerUtils.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 "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/CookieSettings.h"
-#include "nsIConsoleReportCollector.h"
-#include "nsIPrincipal.h"
-#include "nsNetUtil.h"
-#include "nsProxyRelease.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
 using workerinternals::ChannelFromScriptURLMainThread;
 
+class SelfHolder {
+ public:
+  MOZ_IMPLICIT SelfHolder(RemoteWorkerChild* aSelf) : mSelf(aSelf) {
+    MOZ_ASSERT(mSelf);
+  }
+
+  SelfHolder(const SelfHolder&) = default;
+
+  SelfHolder& operator=(const SelfHolder&) = default;
+
+  SelfHolder(SelfHolder&&) = default;
+
+  SelfHolder& operator=(SelfHolder&&) = default;
+
+  ~SelfHolder() {
+    if (!mSelf) {
+      return;
+    }
+
+    nsCOMPtr<nsISerialEventTarget> target = mSelf->GetOwningEventTarget();
+    NS_ProxyRelease("SelfHolder::mSelf", target, mSelf.forget());
+  }
+
+  RemoteWorkerChild* get() const {
+    MOZ_ASSERT(mSelf);
+
+    return mSelf.get();
+  }
+
+  RemoteWorkerChild* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
+    return get();
+  }
+
+  bool operator!() { return !mSelf.get(); }
+
+ private:
+  RefPtr<RemoteWorkerChild> mSelf;
+};
+
 namespace {
 
 class SharedWorkerInterfaceRequestor final : public nsIInterfaceRequestor {
  public:
   NS_DECL_ISUPPORTS
 
   SharedWorkerInterfaceRequestor() {
     // This check must match the code nsDocShell::Create.
@@ -70,20 +129,20 @@ class SharedWorkerInterfaceRequestor fin
 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,
+                                SelfHolder aActor,
                                 const MessagePortIdentifier& aPortIdentifier)
       : WorkerRunnable(aWorkerPrivate),
-        mActor(aActor),
+        mActor(std::move(aActor)),
         mPortIdentifier(aPortIdentifier) {}
 
  private:
   virtual bool WorkerRun(JSContext* aCx,
                          WorkerPrivate* aWorkerPrivate) override {
     mActor->AddPortIdentifier(aCx, aWorkerPrivate, mPortIdentifier);
     return true;
   }
@@ -109,94 +168,177 @@ class MessagePortIdentifierRunnable fina
   }
 
   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                bool aRunResult) override {
     // Silence bad assertions.
     return;
   }
 
-  RefPtr<RemoteWorkerChild> mActor;
+  SelfHolder mActor;
   MessagePortIdentifier mPortIdentifier;
 };
 
-}  // namespace
+class ReleaseWorkerRunnable final : public WorkerRunnable {
+ public:
+  ReleaseWorkerRunnable(RefPtr<WorkerPrivate> aWorkerPrivate,
+                        already_AddRefed<WeakWorkerRef> aWeakRef)
+      : WorkerRunnable(aWorkerPrivate),
+        mWorkerPrivate(std::move(aWorkerPrivate)),
+        mWeakRef(aWeakRef) {}
+
+ private:
+  bool WorkerRun(JSContext*, WorkerPrivate*) override {
+    mWeakRef = nullptr;
+    mWorkerPrivate = nullptr;
+
+    return true;
+  }
+
+  nsresult Cancel() override {
+    mWeakRef = nullptr;
+    mWorkerPrivate = nullptr;
+
+    return NS_OK;
+  }
+
+  RefPtr<WorkerPrivate> mWorkerPrivate;
+  RefPtr<WeakWorkerRef> mWeakRef;
+};
+
+}  // anonymous namespace
 
 class RemoteWorkerChild::InitializeWorkerRunnable final
     : public WorkerRunnable {
  public:
-  InitializeWorkerRunnable(WorkerPrivate* aWorkerPrivate,
-                           RemoteWorkerChild* aActor)
-      : WorkerRunnable(aWorkerPrivate), mActor(aActor) {}
+  InitializeWorkerRunnable(RefPtr<WorkerPrivate> aWorkerPrivate,
+                           SelfHolder aActor)
+      : WorkerRunnable(aWorkerPrivate),
+        mWorkerPrivate(std::move(aWorkerPrivate)),
+        mActor(std::move(aActor)) {
+    MOZ_ASSERT(mWorkerPrivate);
+    MOZ_ASSERT(mActor);
+  }
 
  private:
-  virtual bool WorkerRun(JSContext* aCx,
-                         WorkerPrivate* aWorkerPrivate) override {
-    mActor->InitializeOnWorker(aWorkerPrivate);
+  ~InitializeWorkerRunnable() { MaybeAbort(); }
+
+  bool WorkerRun(JSContext*, WorkerPrivate*) override {
+    mActor->InitializeOnWorker(mWorkerPrivate.forget());
     return true;
   }
 
   nsresult Cancel() override {
-    mActor->CreationFailedOnAnyThread();
-    mActor->ShutdownOnWorker();
+    MaybeAbort();
+
     return WorkerRunnable::Cancel();
   }
 
-  RefPtr<RemoteWorkerChild> mActor;
+  // Slowly running out of synonyms for cancel, abort, terminate, etc...
+  void MaybeAbort() {
+    if (!mWorkerPrivate) {
+      return;
+    }
+
+    nsCOMPtr<nsIEventTarget> target =
+        SystemGroup::EventTargetFor(TaskCategory::Other);
+    NS_ProxyRelease("InitializeWorkerRunnable::mWorkerPrivate", target,
+                    mWorkerPrivate.forget());
+
+    mActor->TransitionStateToTerminated();
+    mActor->CreationFailedOnAnyThread();
+    mActor->ShutdownOnWorker();
+  }
+
+  RefPtr<WorkerPrivate> mWorkerPrivate;
+  SelfHolder mActor;
 };
 
-RemoteWorkerChild::RemoteWorkerChild()
-    : mIPCActive(true), mSharedData("RemoteWorkerChild::mSharedData") {
+RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData)
+    : mState(VariantType<Pending>(), "RemoteWorkerChild::mState"),
+      mIsServiceWorker(aData.serviceWorkerData().type() ==
+                       OptionalServiceWorkerData::TServiceWorkerData),
+      mOwningEventTarget(GetCurrentThreadSerialEventTarget()) {
   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
+  MOZ_ASSERT(mOwningEventTarget);
 }
 
-RemoteWorkerChild::SharedData::SharedData() : mWorkerState(ePending) {}
-
 RemoteWorkerChild::~RemoteWorkerChild() {
-  nsCOMPtr<nsIEventTarget> target =
-      SystemGroup::EventTargetFor(TaskCategory::Other);
+#ifdef DEBUG
+  MOZ_ASSERT(mTerminationPromise.IsEmpty());
+  auto lock = mState.Lock();
+  MOZ_ASSERT(lock->is<Terminated>());
+#endif
+}
 
-  const auto lock = mSharedData.Lock();
-  NS_ProxyRelease("RemoteWorkerChild::mWorkerPrivate", target,
-                  lock->mWorkerPrivate.forget());
+nsISerialEventTarget* RemoteWorkerChild::GetOwningEventTarget() const {
+  return mOwningEventTarget;
 }
 
-void RemoteWorkerChild::ActorDestroy(ActorDestroyReason aWhy) {
-  MOZ_ACCESS_THREAD_BOUND(mLauncherData, data);
-  mIPCActive = false;
-  data->mPendingOps.Clear();
+void RemoteWorkerChild::ActorDestroy(ActorDestroyReason) {
+  Unused << NS_WARN_IF(!mTerminationPromise.IsEmpty());
+  mTerminationPromise.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
+
+  MOZ_ACCESS_THREAD_BOUND(mLauncherData, launcherData);
+  launcherData->mIPCActive = false;
+
+  auto lock = mState.Lock();
+
+  Unused << NS_WARN_IF(!lock->is<Terminated>());
+
+  if (NS_WARN_IF(lock->is<Running>())) {
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+        __func__,
+        [workerPrivate = std::move(lock->as<Running>().mWorkerPrivate),
+         workerRef = std::move(lock->as<Running>().mWorkerRef)]() mutable {
+          RefPtr<ReleaseWorkerRunnable> r =
+              new ReleaseWorkerRunnable(workerPrivate, workerRef.forget());
+
+          Unused << NS_WARN_IF(!r->Dispatch());
+
+          workerPrivate->Cancel();
+        });
+
+    MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
+  }
+
+  *lock = VariantType<Terminated>();
 }
 
 void RemoteWorkerChild::ExecWorker(const RemoteWorkerData& aData) {
-  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
-  MOZ_ASSERT(mIPCActive);
+#ifdef DEBUG
+  MOZ_ASSERT(GetOwningEventTarget()->IsOnCurrentThread());
+  MOZ_ACCESS_THREAD_BOUND(mLauncherData, launcherData);
+  MOZ_ASSERT(launcherData->mIPCActive);
+#endif
 
-  RefPtr<RemoteWorkerChild> self = this;
-  nsCOMPtr<nsIRunnable> r =
-      NS_NewRunnableFunction("RemoteWorkerChild::ExecWorker", [self, aData]() {
-        nsresult rv = self->ExecWorkerOnMainThread(aData);
+  SelfHolder self = this;
+
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      __func__, [self = std::move(self), data = aData]() mutable {
+        nsresult rv = self->ExecWorkerOnMainThread(std::move(data));
+
         if (NS_WARN_IF(NS_FAILED(rv))) {
           self->CreationFailedOnAnyThread();
         }
       });
 
-  nsCOMPtr<nsIEventTarget> target =
-      SystemGroup::EventTargetFor(TaskCategory::Other);
-  target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+  MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
 }
 
-nsresult RemoteWorkerChild::ExecWorkerOnMainThread(
-    const RemoteWorkerData& aData) {
+nsresult RemoteWorkerChild::ExecWorkerOnMainThread(RemoteWorkerData&& aData) {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Ensure that the IndexedDatabaseManager is initialized
   Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
 
   nsresult rv = NS_OK;
 
+  auto scopeExit = MakeScopeExit([&] { TransitionStateToTerminated(); });
+
   nsCOMPtr<nsIPrincipal> principal =
       PrincipalInfoToPrincipal(aData.principalInfo(), &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIPrincipal> loadingPrincipal =
       PrincipalInfoToPrincipal(aData.loadingPrincipalInfo(), &rv);
@@ -259,124 +401,236 @@ nsresult RemoteWorkerChild::ExecWorkerOn
   }
 
   rv = info.SetPrincipalsAndCSPOnMainThread(
       info.mPrincipal, info.mStoragePrincipal, info.mLoadGroup, info.mCSP);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // Top level workers' main script use the document charset for the script
-  // uri encoding.
-  rv = ChannelFromScriptURLMainThread(
-      info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup,
-      info.mResolvedScriptURI, clientInfo,
-      aData.isSharedWorker() ? nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER
-                             : nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER,
-      info.mCookieSettings, info.mReferrerInfo, getter_AddRefs(info.mChannel));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  if (mIsServiceWorker) {
+    ServiceWorkerData& data = aData.serviceWorkerData().get_ServiceWorkerData();
+
+    nsCOMPtr<nsIPermissionManager> permissionManager =
+        services::GetPermissionManager();
+
+    for (auto& keyAndPermissions : data.permissionsByKey()) {
+      permissionManager->SetPermissionsWithKey(keyAndPermissions.key(),
+                                               keyAndPermissions.permissions());
+    }
+
+    info.mServiceWorkerCacheName = data.cacheName();
+    info.mServiceWorkerDescriptor.emplace(data.descriptor());
+    info.mServiceWorkerRegistrationDescriptor.emplace(
+        data.registrationDescriptor());
+    info.mLoadFlags = static_cast<nsLoadFlags>(data.loadFlags());
+  } else {
+    // Top level workers' main script use the document charset for the script
+    // uri encoding.
+    rv = ChannelFromScriptURLMainThread(
+        info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup,
+        info.mResolvedScriptURI, clientInfo,
+        nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER, info.mCookieSettings,
+        info.mReferrerInfo, getter_AddRefs(info.mChannel));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   AutoJSAPI jsapi;
   jsapi.Init();
 
   ErrorResult error;
-  const auto lock = mSharedData.Lock();
-  lock->mWorkerPrivate = WorkerPrivate::Constructor(
+  RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
       jsapi.cx(), aData.originalScriptURL(), false,
-      aData.isSharedWorker() ? WorkerTypeShared : WorkerTypeService,
-      aData.name(), VoidCString(), &info, error);
+      mIsServiceWorker ? WorkerTypeService : WorkerTypeShared, aData.name(),
+      VoidCString(), &info, error);
+
   if (NS_WARN_IF(error.Failed())) {
-    return error.StealNSResult();
+    MOZ_ASSERT(!workerPrivate);
+
+    rv = error.StealNSResult();
+    return rv;
+  }
+
+  if (mIsServiceWorker) {
+  } else {
+    workerPrivate->SetRemoteWorkerController(this);
   }
 
   RefPtr<InitializeWorkerRunnable> runnable =
-      new InitializeWorkerRunnable(lock->mWorkerPrivate, this);
+      new InitializeWorkerRunnable(std::move(workerPrivate), SelfHolder(this));
+
   if (NS_WARN_IF(!runnable->Dispatch())) {
-    return NS_ERROR_FAILURE;
+    rv = NS_ERROR_FAILURE;
+    return rv;
   }
 
-  lock->mWorkerPrivate->SetRemoteWorkerController(this);
+  scopeExit.release();
+
   return NS_OK;
 }
 
-void RemoteWorkerChild::InitializeOnWorker(WorkerPrivate* aWorkerPrivate) {
-  MOZ_ASSERT(aWorkerPrivate);
-  aWorkerPrivate->AssertIsOnWorkerThread();
+void RemoteWorkerChild::InitializeOnWorker(
+    already_AddRefed<WorkerPrivate> aWorkerPrivate) {
+  {
+    auto lock = mState.Lock();
+
+    if (lock->is<PendingTerminated>()) {
+      nsCOMPtr<nsIEventTarget> target =
+          SystemGroup::EventTargetFor(TaskCategory::Other);
+      NS_ProxyRelease(__func__, target, std::move(aWorkerPrivate));
+
+      TransitionStateToTerminated(lock.ref());
+      ShutdownOnWorker();
+      return;
+    }
+  }
+
+  RefPtr<WorkerPrivate> workerPrivate = aWorkerPrivate;
+  MOZ_ASSERT(workerPrivate);
+  workerPrivate->AssertIsOnWorkerThread();
 
   RefPtr<RemoteWorkerChild> self = this;
-  {
-    const auto lock = mSharedData.Lock();
-    mWorkerRef = WeakWorkerRef::Create(lock->mWorkerPrivate,
-                                       [self]() { self->ShutdownOnWorker(); });
-  }
+  ThreadSafeWeakPtr<RemoteWorkerChild> selfWeakRef(self);
+
+  auto scopeExit = MakeScopeExit([&] {
+    MOZ_ASSERT(self);
+
+    NS_ProxyRelease(__func__, mOwningEventTarget, self.forget());
+  });
+
+  RefPtr<WeakWorkerRef> workerRef = WeakWorkerRef::Create(
+      workerPrivate, [selfWeakRef = std::move(selfWeakRef)]() mutable {
+        RefPtr<RemoteWorkerChild> self(selfWeakRef);
 
-  if (NS_WARN_IF(!mWorkerRef)) {
+        if (NS_WARN_IF(!self)) {
+          return;
+        }
+
+        self->TransitionStateToTerminated();
+        self->ShutdownOnWorker();
+
+        nsCOMPtr<nsISerialEventTarget> target = self->GetOwningEventTarget();
+        NS_ProxyRelease(__func__, target, self.forget());
+      });
+
+  if (NS_WARN_IF(!workerRef)) {
+    TransitionStateToTerminated();
     CreationFailedOnAnyThread();
     ShutdownOnWorker();
+
     return;
   }
 
+  TransitionStateToRunning(workerPrivate.forget(), workerRef.forget());
   CreationSucceededOnAnyThread();
 }
 
 void RemoteWorkerChild::ShutdownOnWorker() {
-  const auto lock = mSharedData.Lock();
-  MOZ_ASSERT(lock->mWorkerPrivate);
-  lock->mWorkerPrivate->AssertIsOnWorkerThread();
+  RefPtr<RemoteWorkerChild> self = this;
+
+  nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableFunction(__func__, [self = std::move(self)] {
+        MOZ_ACCESS_THREAD_BOUND(self->mLauncherData, launcherData);
+
+        if (!launcherData->mIPCActive) {
+          return;
+        }
+
+        launcherData->mIPCActive = false;
+        Unused << self->SendClose();
+      });
+
+  GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+}
 
-  // This will release the worker.
-  mWorkerRef = nullptr;
+RefPtr<GenericNonExclusivePromise> RemoteWorkerChild::GetTerminationPromise() {
+  MOZ_ASSERT(GetOwningEventTarget()->IsOnCurrentThread());
+
+  return mTerminationPromise.Ensure(__func__);
+}
+
+void RemoteWorkerChild::CreationSucceededOnAnyThread() {
+  CreationSucceededOrFailedOnAnyThread(true);
+}
 
-  nsCOMPtr<nsIEventTarget> target =
-      SystemGroup::EventTargetFor(TaskCategory::Other);
+void RemoteWorkerChild::CreationFailedOnAnyThread() {
+  CreationSucceededOrFailedOnAnyThread(false);
+}
 
-  NS_ProxyRelease("RemoteWorkerChild::mWorkerPrivate", target,
-                  lock->mWorkerPrivate.forget());
+void RemoteWorkerChild::CreationSucceededOrFailedOnAnyThread(
+    bool aDidCreationSucceed) {
+#ifdef DEBUG
+  auto lock = mState.Lock();
+  MOZ_ASSERT_IF(aDidCreationSucceed, lock->is<Running>());
+  MOZ_ASSERT_IF(!aDidCreationSucceed, lock->is<Terminated>());
+#endif
 
   RefPtr<RemoteWorkerChild> self = this;
-  nsCOMPtr<nsIRunnable> r =
-      NS_NewRunnableFunction("RemoteWorkerChild::ShutdownOnWorker",
-                             [self]() { self->WorkerTerminated(); });
+
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      __func__,
+      [self = std::move(self), didCreationSucceed = aDidCreationSucceed] {
+        MOZ_ACCESS_THREAD_BOUND(self->mLauncherData, launcherData);
 
-  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+        if (!launcherData->mIPCActive) {
+          return;
+        }
+
+        Unused << self->SendCreated(didCreationSucceed);
+      });
+
+  Unused << GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
 }
 
-void RemoteWorkerChild::WorkerTerminated() {
-  MOZ_ACCESS_THREAD_BOUND(mLauncherData, data);
+void RemoteWorkerChild::CloseWorkerOnMainThread(State& aState) {
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!aState.is<PendingTerminated>());
 
-  {
-    const auto lock = mSharedData.Lock();
-    lock->mWorkerState = eTerminated;
-  }
-  data->mPendingOps.Clear();
-
-  if (!mIPCActive) {
+  if (aState.is<Pending>()) {
+    TransitionStateToPendingTerminated(aState);
     return;
   }
 
-  Unused << SendClose();
-  mIPCActive = false;
+  // The holder will be notified by this.
+  if (aState.is<Running>()) {
+    aState.as<Running>().mWorkerPrivate->Cancel();
+  }
+}
+
+/**
+ * Error reporting method
+ */
+void RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue) {
+  MOZ_ASSERT(GetOwningEventTarget()->IsOnCurrentThread());
+
+  MOZ_ACCESS_THREAD_BOUND(mLauncherData, launcherData);
+
+  if (!launcherData->mIPCActive) {
+    return;
+  }
+
+  Unused << SendError(aValue);
 }
 
 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); });
+      [self = std::move(self), aError]() { self->ErrorPropagation(aError); });
 
-  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+  GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
 }
 
 void RemoteWorkerChild::ErrorPropagationOnMainThread(
     const WorkerErrorReport* aReport, bool aIsErrorEvent) {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnMainThread();
 
   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));
@@ -388,57 +642,24 @@ void RemoteWorkerChild::ErrorPropagation
     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);
-}
+      [self = std::move(self), value]() { self->ErrorPropagation(value); });
 
-void RemoteWorkerChild::CloseWorkerOnMainThread() {
-  MOZ_ASSERT(NS_IsMainThread());
-  const auto lock = mSharedData.Lock();
-
-  if (lock->mWorkerState == ePending) {
-    lock->mWorkerState = ePendingTerminated;
-    // Already released.
-    return;
-  }
-
-  // The holder will be notified by this.
-  if (lock->mWorkerState == eRunning) {
-    // FIXME: mWorkerState transition and each state's associated data should
-    // be improved/fixed in bug 1231213. `!lock->mWorkerPrivate` implies that
-    // the worker state is effectively `eTerminated.`
-    if (!lock->mWorkerPrivate) {
-      return;
-    }
-
-    lock->mWorkerPrivate->Cancel();
-  }
+  GetOwningEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
 }
 
 void RemoteWorkerChild::FlushReportsOnMainThread(
     nsIConsoleReportCollector* aReporter) {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnMainThread();
 
   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;
@@ -448,199 +669,258 @@ void RemoteWorkerChild::FlushReportsOnMa
   if (reportErrorToBrowserConsole) {
     aReporter->FlushReportsToConsole(0);
     return;
   }
 
   aReporter->ClearConsoleReports();
 }
 
-IPCResult RemoteWorkerChild::RecvExecOp(const RemoteWorkerOp& aOp) {
-  const auto lock = mSharedData.Lock();
-  return ExecuteOperation(aOp, lock);
+/**
+ * Worker state transition methods
+ */
+RemoteWorkerChild::Running::~Running() {
+  if (!mWorkerPrivate) {
+    return;
+  }
+
+  nsCOMPtr<nsIEventTarget> target =
+      SystemGroup::EventTargetFor(TaskCategory::Other);
+  NS_ProxyRelease("RemoteWorkerChild::Running::mWorkerPrivate", target,
+                  mWorkerPrivate.forget());
+}
+
+void RemoteWorkerChild::TransitionStateToPendingTerminated(State& aState) {
+  MOZ_ASSERT(aState.is<Pending>());
+
+  CancelAllPendingOps(aState);
+
+  aState = VariantType<PendingTerminated>();
 }
 
-template <typename T>
-IPCResult RemoteWorkerChild::ExecuteOperation(const RemoteWorkerOp& aOp,
-                                              const T& aLock) {
-  MOZ_ACCESS_THREAD_BOUND(mLauncherData, data);
+void RemoteWorkerChild::TransitionStateToRunning(
+    already_AddRefed<WorkerPrivate> aWorkerPrivate,
+    already_AddRefed<WeakWorkerRef> aWorkerRef) {
+  RefPtr<WorkerPrivate> workerPrivate = aWorkerPrivate;
+  MOZ_ASSERT(workerPrivate);
+
+  RefPtr<WeakWorkerRef> workerRef = aWorkerRef;
+  MOZ_ASSERT(workerRef);
+
+  auto lock = mState.Lock();
 
-  if (!mIPCActive) {
-    return IPC_OK();
-  }
+  MOZ_ASSERT(lock->is<Pending>());
+
+  auto pendingOps = std::move(lock->as<Pending>().mPendingOps);
+
+  /**
+   * I'd initialize the WorkerPrivate and WeakWorkerRef in the constructor,
+   * but mozilla::Variant attempts to call the thread-unsafe `AddRef()` on
+   * WorkerPrivate.
+   */
+  *lock = VariantType<Running>();
+  lock->as<Running>().mWorkerPrivate = std::move(workerPrivate);
+  lock->as<Running>().mWorkerRef = std::move(workerRef);
 
-  // The worker is not ready yet.
-  if (aLock->mWorkerState == ePending) {
-    data->mPendingOps.AppendElement(aOp);
-    return IPC_OK();
-  }
+  SelfHolder self = this;
+
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      __func__, [pendingOps = std::move(pendingOps), self = std::move(self)]() {
+        for (auto& op : pendingOps) {
+          auto lock = self->mState.Lock();
+
+          DebugOnly<bool> started = op->MaybeStart(self.get(), lock.ref());
+          MOZ_ASSERT(started);
+        }
+      });
 
-  if (aLock->mWorkerState == eTerminated ||
-      aLock->mWorkerState == ePendingTerminated) {
-    // No op.
-    return IPC_OK();
+  MOZ_ALWAYS_SUCCEEDS(
+      mOwningEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
+}
+
+void RemoteWorkerChild::TransitionStateToTerminated() {
+  auto lock = mState.Lock();
+
+  TransitionStateToTerminated(lock.ref());
+}
+
+void RemoteWorkerChild::TransitionStateToTerminated(State& aState) {
+  if (aState.is<Pending>()) {
+    CancelAllPendingOps(aState);
   }
 
-  MOZ_ASSERT(aLock->mWorkerState == eRunning);
+  mTerminationPromise.ResolveIfExists(true, __func__);
+
+  aState = VariantType<Terminated>();
+}
+
+/**
+ * Operation execution classes/methods
+ */
+class RemoteWorkerChild::SharedWorkerOp : public RemoteWorkerChild::Op {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerOp, override)
+
+  explicit SharedWorkerOp(RemoteWorkerOp&& aOp) : mOp(std::move(aOp)) {}
+
+  bool MaybeStart(RemoteWorkerChild* aOwner,
+                  RemoteWorkerChild::State& aState) override {
+    MOZ_ASSERT(!mStarted);
+    MOZ_ASSERT(aOwner);
+
+    MOZ_ACCESS_THREAD_BOUND(aOwner->mLauncherData, launcherData);
+
+    if (NS_WARN_IF(!launcherData->mIPCActive)) {
+      Unused << NS_WARN_IF(!aState.is<Terminated>());
+
+#ifdef DEBUG
+      mStarted = true;
+#endif
+
+      return true;
+    }
+
+    if (aState.is<Pending>() && !IsTerminationOp()) {
+      return false;
+    }
 
-  // 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;
+    if (aState.is<PendingTerminated>() || aState.is<Terminated>()) {
+#ifdef DEBUG
+      mStarted = true;
+#endif
+
+      return true;
+    }
+
+    MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
+
+    RefPtr<SharedWorkerOp> self = this;
+    SelfHolder owner = aOwner;
+
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
-        "RemoteWorkerChild::RecvExecOp",
-        [self, aOp]() { self->RecvExecOpOnMainThread(aOp); });
+        __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
+          {
+            auto lock = owner->mState.Lock();
+
+            if (NS_WARN_IF(lock->is<Terminated>())) {
+              self->Cancel();
+              return;
+            }
+          }
 
-    nsCOMPtr<nsIEventTarget> target =
-        SystemGroup::EventTargetFor(TaskCategory::Other);
-    target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
-    return IPC_OK();
+          self->Exec(owner);
+        });
+
+    MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
+
+#ifdef DEBUG
+    mStarted = true;
+#endif
+
+    return true;
   }
 
-  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
-    const RemoteWorkerPortIdentifierOp& op =
-        aOp.get_RemoteWorkerPortIdentifierOp();
-    RefPtr<MessagePortIdentifierRunnable> runnable =
-        new MessagePortIdentifierRunnable(aLock->mWorkerPrivate, this,
-                                          op.portIdentifier());
-    if (NS_WARN_IF(!runnable->Dispatch())) {
-      ErrorPropagation(NS_ERROR_FAILURE);
-    }
-    return IPC_OK();
+  void Cancel() override {
+    MOZ_ASSERT(!mStarted);
+
+#ifdef DEBUG
+    mStarted = true;
+#endif
   }
 
-  MOZ_CRASH("Unknown operation.");
+ private:
+  ~SharedWorkerOp() { MOZ_ASSERT(mStarted); }
 
-  return IPC_OK();
-}
-
-void RemoteWorkerChild::RecvExecOpOnMainThread(const RemoteWorkerOp& aOp) {
-  MOZ_ASSERT(NS_IsMainThread());
+  bool IsTerminationOp() const {
+    return mOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp;
+  }
 
-  {
-    const auto lock = mSharedData.Lock();
+  void Exec(SelfHolder& aOwner) {
+    using Running = RemoteWorkerChild::Running;
+
+    AssertIsOnMainThread();
 
-    if (aOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp) {
-      if (lock->mWorkerPrivate) {
-        lock->mWorkerPrivate->ParentWindowPaused();
-      }
+    auto lock = aOwner->mState.Lock();
+
+    MOZ_ASSERT(lock->is<Running>() || IsTerminationOp());
+
+    if (IsTerminationOp()) {
+      aOwner->CloseWorkerOnMainThread(lock.ref());
       return;
     }
 
-    if (aOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp) {
-      if (lock->mWorkerPrivate) {
-        lock->mWorkerPrivate->ParentWindowResumed();
-      }
-      return;
-    }
+    RefPtr<WorkerPrivate> workerPrivate = lock->as<Running>().mWorkerPrivate;
+
+    MOZ_ASSERT(workerPrivate);
 
-    if (aOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp) {
-      if (lock->mWorkerPrivate) {
-        lock->mWorkerPrivate->Freeze(nullptr);
+    if (mOp.type() == RemoteWorkerOp::TRemoteWorkerSuspendOp) {
+      workerPrivate->ParentWindowPaused();
+    } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerResumeOp) {
+      workerPrivate->ParentWindowResumed();
+    } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerFreezeOp) {
+      workerPrivate->Freeze(nullptr);
+    } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp) {
+      workerPrivate->Thaw(nullptr);
+    } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerPortIdentifierOp) {
+      RefPtr<MessagePortIdentifierRunnable> r =
+          new MessagePortIdentifierRunnable(
+              workerPrivate, aOwner,
+              mOp.get_RemoteWorkerPortIdentifierOp().portIdentifier());
+
+      if (NS_WARN_IF(!r->Dispatch())) {
+        aOwner->ErrorPropagation(NS_ERROR_FAILURE);
       }
-      return;
-    }
-
-    if (aOp.type() == RemoteWorkerOp::TRemoteWorkerThawOp) {
-      if (lock->mWorkerPrivate) {
-        lock->mWorkerPrivate->Thaw(nullptr);
-      }
-      return;
+    } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerAddWindowIDOp) {
+      aOwner->mWindowIDs.AppendElement(
+          mOp.get_RemoteWorkerAddWindowIDOp().windowID());
+    } else if (mOp.type() == RemoteWorkerOp::TRemoteWorkerRemoveWindowIDOp) {
+      aOwner->mWindowIDs.RemoveElement(
+          mOp.get_RemoteWorkerRemoveWindowIDOp().windowID());
+    } else {
+      MOZ_CRASH("Unknown RemoteWorkerOp type!");
     }
   }
 
-  if (aOp.type() == RemoteWorkerOp::TRemoteWorkerTerminateOp) {
-    CloseWorkerOnMainThread();
-    return;
-  }
+  RemoteWorkerOp mOp;
 
-  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.");
-}
+#ifdef DEBUG
+  bool mStarted = false;
+#endif
+};
 
 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(); });
+void RemoteWorkerChild::CancelAllPendingOps(State& aState) {
+  MOZ_ASSERT(aState.is<Pending>());
 
-  RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+  auto pendingOps = std::move(aState.as<Pending>().mPendingOps);
+
+  for (auto& op : pendingOps) {
+    op->Cancel();
+  }
 }
 
-void RemoteWorkerChild::CreationSucceeded() {
-  MOZ_ACCESS_THREAD_BOUND(mLauncherData, data);
-
-  // The worker is created but we need to terminate it already.
-  const auto lock = mSharedData.Lock();
-  if (lock->mWorkerState == ePendingTerminated) {
-    RefPtr<RemoteWorkerChild> self = this;
-    nsCOMPtr<nsIRunnable> r =
-        NS_NewRunnableFunction("RemoteWorkerChild::CreationSucceeded",
-                               [self]() { self->CloseWorkerOnMainThread(); });
+void RemoteWorkerChild::MaybeStartOp(RefPtr<Op>&& aOp) {
+  MOZ_ASSERT(aOp);
 
-    nsCOMPtr<nsIEventTarget> target =
-        SystemGroup::EventTargetFor(TaskCategory::Other);
-    target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
-    return;
-  }
-
-  lock->mWorkerState = eRunning;
+  auto lock = mState.Lock();
 
-  if (!mIPCActive) {
-    return;
+  if (!aOp->MaybeStart(this, lock.ref())) {
+    lock->as<Pending>().mPendingOps.AppendElement(std::move(aOp));
   }
-
-  for (const RemoteWorkerOp& op : data->mPendingOps) {
-    ExecuteOperation(op, lock);
-  }
-
-  data->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);
-}
+IPCResult RemoteWorkerChild::RecvExecOp(RemoteWorkerOp&& aOp) {
+  MOZ_ASSERT(!mIsServiceWorker);
 
-void RemoteWorkerChild::CreationFailed() {
-  MOZ_ACCESS_THREAD_BOUND(mLauncherData, data);
+  MaybeStartOp(new SharedWorkerOp(std::move(aOp)));
 
-  const auto lock = mSharedData.Lock();
-  lock->mWorkerState = eTerminated;
-  data->mPendingOps.Clear();
-
-  if (!mIPCActive) {
-    return;
-  }
-
-  Unused << SendCreated(false);
+  return IPC_OK();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/workers/remoteworkers/RemoteWorkerChild.h
+++ b/dom/workers/remoteworkers/RemoteWorkerChild.h
@@ -2,124 +2,151 @@
 /* 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/. */
 
 #ifndef mozilla_dom_RemoteWorkerChild_h
 #define mozilla_dom_RemoteWorkerChild_h
 
-#include "mozilla/dom/PRemoteWorkerChild.h"
+#include "nsCOMPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
 #include "mozilla/DataMutex.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
 #include "mozilla/ThreadBound.h"
-#include "mozilla/UniquePtr.h"
-#include "nsISupportsImpl.h"
+#include "mozilla/ThreadSafeWeakPtr.h"
+#include "mozilla/dom/PRemoteWorkerChild.h"
+#include "mozilla/dom/ServiceWorkerOpArgs.h"
 
+class nsISerialEventTarget;
 class nsIConsoleReportCollector;
 
 namespace mozilla {
 namespace dom {
 
+class ErrorValue;
 class RemoteWorkerData;
 class WeakWorkerRef;
 class WorkerErrorReport;
 class WorkerPrivate;
-class OptionalMessagePortIdentifier;
 
-class RemoteWorkerChild final : public PRemoteWorkerChild {
+class RemoteWorkerChild final
+    : public SupportsThreadSafeWeakPtr<RemoteWorkerChild>,
+      public PRemoteWorkerChild {
   friend class PRemoteWorkerChild;
 
  public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteWorkerChild)
+  MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(RemoteWorkerChild)
+
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(RemoteWorkerChild)
 
-  RemoteWorkerChild();
+  explicit RemoteWorkerChild(const RemoteWorkerData& aData);
+
+  ~RemoteWorkerChild();
+
+  nsISerialEventTarget* GetOwningEventTarget() const;
 
   void ExecWorker(const RemoteWorkerData& aData);
 
-  void InitializeOnWorker(WorkerPrivate* aWorkerPrivate);
+  void ErrorPropagationOnMainThread(const WorkerErrorReport* aReport,
+                                    bool aIsErrorEvent);
 
-  void ShutdownOnWorker();
+  void FlushReportsOnMainThread(nsIConsoleReportCollector* aReporter);
 
   void AddPortIdentifier(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                          const MessagePortIdentifier& aPortIdentifier);
 
-  void ErrorPropagationOnMainThread(const WorkerErrorReport* aReport,
-                                    bool aIsErrorEvent);
+  RefPtr<GenericNonExclusivePromise> GetTerminationPromise();
 
   void CloseWorkerOnMainThread();
 
-  void FlushReportsOnMainThread(nsIConsoleReportCollector* aReporter);
-
  private:
   class InitializeWorkerRunnable;
 
-  ~RemoteWorkerChild();
+  class Op;
+  class SharedWorkerOp;
+
+  struct Pending {
+    nsTArray<RefPtr<Op>> mPendingOps;
+  };
+
+  struct PendingTerminated {};
 
-  void ActorDestroy(ActorDestroyReason aWhy) override;
+  struct Running {
+    ~Running();
 
-  mozilla::ipc::IPCResult RecvExecOp(const RemoteWorkerOp& aOp);
+    RefPtr<WorkerPrivate> mWorkerPrivate;
+    RefPtr<WeakWorkerRef> mWorkerRef;
+  };
+
+  struct Terminated {};
+
+  using State = Variant<Pending, Running, PendingTerminated, Terminated>;
+
+  DataMutex<State> mState;
 
-  // This member is a function template because DataMutex<SharedData>::AutoLock
-  // is private, yet it must be passed by const reference into ExecuteOperation.
-  // There should only be one instantiation of this template.
-  template <typename T>
-  mozilla::ipc::IPCResult ExecuteOperation(const RemoteWorkerOp&,
-                                           const T& aLock);
+  class Op {
+   public:
+    NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+    virtual ~Op() = default;
+
+    virtual bool MaybeStart(RemoteWorkerChild* aOwner, State& aState) = 0;
+
+    virtual void Cancel() = 0;
+  };
+
+  void ActorDestroy(ActorDestroyReason) override;
 
-  void RecvExecOpOnMainThread(const RemoteWorkerOp& aOp);
+  mozilla::ipc::IPCResult RecvExecOp(RemoteWorkerOp&& aOp);
+
+  nsresult ExecWorkerOnMainThread(RemoteWorkerData&& aData);
+
+  void InitializeOnWorker(already_AddRefed<WorkerPrivate> aWorkerPrivate);
+
+  void ShutdownOnWorker();
 
-  nsresult ExecWorkerOnMainThread(const RemoteWorkerData& aData);
+  void CreationSucceededOnAnyThread();
+
+  void CreationFailedOnAnyThread();
+
+  void CreationSucceededOrFailedOnAnyThread(bool aDidCreationSucceed);
+
+  void CloseWorkerOnMainThread(State& aState);
 
   void ErrorPropagation(const ErrorValue& aValue);
 
   void ErrorPropagationDispatch(nsresult aError);
 
-  void CreationSucceededOnAnyThread();
+  void TransitionStateToPendingTerminated(State& aState);
 
-  void CreationSucceeded();
+  void TransitionStateToRunning(already_AddRefed<WorkerPrivate> aWorkerPrivate,
+                                already_AddRefed<WeakWorkerRef> aWorkerRef);
+
+  void TransitionStateToTerminated();
+
+  void TransitionStateToTerminated(State& aState);
 
-  void CreationFailedOnAnyThread();
+  void CancelAllPendingOps(State& aState);
+
+  void MaybeStartOp(RefPtr<Op>&& aOp);
 
-  void CreationFailed();
+  MozPromiseHolder<GenericNonExclusivePromise> mTerminationPromise;
 
-  void WorkerTerminated();
+  const bool mIsServiceWorker;
+  const nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
 
   // Touched on main-thread only.
   nsTArray<uint64_t> mWindowIDs;
 
-  RefPtr<WeakWorkerRef> mWorkerRef;
-  bool mIPCActive;
-
-  enum WorkerState {
-    // CreationSucceeded/CreationFailed not called yet.
-    ePending,
-
-    // The worker is not created yet, but we want to terminate as soon as
-    // possible.
-    ePendingTerminated,
-
-    // Worker up and running.
-    eRunning,
-
-    // Worker terminated.
-    eTerminated,
-  };
-
-  struct SharedData {
-    SharedData();
-
-    RefPtr<WorkerPrivate> mWorkerPrivate;
-    WorkerState mWorkerState;
-  };
-
-  DataMutex<SharedData> mSharedData;
-
-  // Touched only on the owning thread (Worker Launcher).
   struct LauncherBoundData {
-    nsTArray<RemoteWorkerOp> mPendingOps;
+    bool mIPCActive = true;
   };
 
   ThreadBound<LauncherBoundData> mLauncherData;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/workers/remoteworkers/RemoteWorkerParent.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.cpp
@@ -131,17 +131,18 @@ void RemoteWorkerParent::MaybeSendDelete
 IPCResult RemoteWorkerParent::RecvClose() {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 
   if (mController) {
     mController->WorkerTerminated();
   }
 
-  Unused << Send__delete__(this);
+  MaybeSendDelete();
+
   return IPC_OK();
 }
 
 void RemoteWorkerParent::SetController(RemoteWorkerController* aController) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 
   mController = aController;
--- a/dom/workers/remoteworkers/RemoteWorkerTypes.ipdlh
+++ b/dom/workers/remoteworkers/RemoteWorkerTypes.ipdlh
@@ -61,17 +61,16 @@ struct RemoteWorkerData
   bool isSecureContext;
 
   IPCClientInfo? clientInfo;
 
   nsIReferrerInfo referrerInfo;
 
   StorageAccess storageAccess;
 
-  bool isSharedWorker;
   OptionalServiceWorkerData serviceWorkerData;
 };
 
 // ErrorData/ErrorDataNote correspond to WorkerErrorReport/WorkerErrorNote
 // which in turn correspond to JSErrorReport/JSErrorNotes which allows JS to
 // report complicated errors such as redeclarations that involve multiple
 // distinct lines.  For more generic error-propagation IPC structures, see bug
 // 1357463 on making ErrorResult usable over IPC.
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -320,17 +320,17 @@ dom::PPendingIPCBlobChild* BackgroundChi
 bool BackgroundChildImpl::DeallocPPendingIPCBlobChild(
     dom::PPendingIPCBlobChild* aActor) {
   delete aActor;
   return true;
 }
 
 dom::PRemoteWorkerChild* BackgroundChildImpl::AllocPRemoteWorkerChild(
     const RemoteWorkerData& aData) {
-  RefPtr<dom::RemoteWorkerChild> agent = new dom::RemoteWorkerChild();
+  RefPtr<dom::RemoteWorkerChild> agent = new dom::RemoteWorkerChild(aData);
   return agent.forget().take();
 }
 
 IPCResult BackgroundChildImpl::RecvPRemoteWorkerConstructor(
     PRemoteWorkerChild* aActor, const RemoteWorkerData& aData) {
   dom::RemoteWorkerChild* actor = static_cast<dom::RemoteWorkerChild*>(aActor);
   actor->ExecWorker(aData);
   return IPC_OK();