Bug 1231213 - Refactor RemoteWorkerChild to handle Service Workers' and Shared Workers' operations and to make its state data/transitions safer. r=asuth
authorPerry Jiang <perry@mozilla.com>
Thu, 15 Aug 2019 17:26:58 +0000
changeset 488305 20b5efa4517d58f49e462d7d9ec9391308b330b6
parent 488304 183b54cffbd1fa38734c985e3b9bbe896bc13ae0
child 488306 6e4c363ba32ab731a19ec4fda8ec33bf3617825d
push id113906
push userncsoregi@mozilla.com
push dateFri, 16 Aug 2019 04:07:24 +0000
treeherdermozilla-inbound@d887276421d3 [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,256 @@ 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 {
+#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();