Bug 1231213 - Refactor RemoteWorkerController to handle Service Workers' and Shared Workers' operations. r=asuth
☠☠ backed out by 3cf55b7f12f2 ☠ ☠
authorPerry Jiang <perry@mozilla.com>
Wed, 14 Aug 2019 16:19:59 +0000
changeset 488070 0b03a19a6dc141f49ead7555b200e5b7498124c0
parent 488069 11f010e6d6e7166b92c2638832896962bd8a71a7
child 488071 666bf42600469276c778a9ab9a4e1324eccd02ea
push id113900
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:53:50 +0000
treeherdermozilla-inbound@0db07ff50ab5 [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 RemoteWorkerController to handle Service Workers' and Shared Workers' operations. r=asuth Differential Revision: https://phabricator.services.mozilla.com/D26169
dom/serviceworkers/moz.build
dom/workers/remoteworkers/RemoteWorkerController.cpp
dom/workers/remoteworkers/RemoteWorkerController.h
dom/workers/remoteworkers/RemoteWorkerParent.cpp
dom/workers/remoteworkers/RemoteWorkerParent.h
--- a/dom/serviceworkers/moz.build
+++ b/dom/serviceworkers/moz.build
@@ -6,16 +6,17 @@
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: Service Workers")
 
 # Public stuff.
 EXPORTS.mozilla.dom += [
     'ServiceWorker.h',
     'ServiceWorkerActors.h',
+    'ServiceWorkerCloneData.h',
     'ServiceWorkerContainer.h',
     'ServiceWorkerDescriptor.h',
     'ServiceWorkerEvents.h',
     'ServiceWorkerInfo.h',
     'ServiceWorkerInterceptController.h',
     'ServiceWorkerIPCUtils.h',
     'ServiceWorkerManager.h',
     'ServiceWorkerManagerChild.h',
--- a/dom/workers/remoteworkers/RemoteWorkerController.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerController.cpp
@@ -1,34 +1,43 @@
 /* -*- 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 "mozilla/dom/MessagePort.h"
+#include "RemoteWorkerController.h"
+
+#include <utility>
+
+#include "nsDebug.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/RemoteWorkerTypes.h"
+#include "mozilla/dom/ServiceWorkerCloneData.h"
 #include "mozilla/ipc/BackgroundParent.h"
-#include "RemoteWorkerController.h"
+#include "RemoteWorkerControllerParent.h"
 #include "RemoteWorkerManager.h"
 #include "RemoteWorkerParent.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
 /* static */
 already_AddRefed<RemoteWorkerController> RemoteWorkerController::Create(
     const RemoteWorkerData& aData, RemoteWorkerObserver* aObserver,
     base::ProcessId aProcessId) {
+  AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aObserver);
 
   RefPtr<RemoteWorkerController> controller =
       new RemoteWorkerController(aData, aObserver);
 
   RefPtr<RemoteWorkerManager> manager = RemoteWorkerManager::GetOrCreate();
   MOZ_ASSERT(manager);
 
@@ -40,294 +49,370 @@ already_AddRefed<RemoteWorkerController>
 RemoteWorkerController::RemoteWorkerController(const RemoteWorkerData& aData,
                                                RemoteWorkerObserver* aObserver)
     : mObserver(aObserver),
       mState(ePending),
       mIsServiceWorker(aData.serviceWorkerData().type() ==
                        OptionalServiceWorkerData::TServiceWorkerData) {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 RemoteWorkerController::~RemoteWorkerController() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_DIAGNOSTIC_ASSERT(mPendingOps.IsEmpty());
 }
 
 void RemoteWorkerController::SetWorkerActor(RemoteWorkerParent* aActor) {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(!mActor);
   MOZ_ASSERT(aActor);
 
   mActor = aActor;
 }
 
+void RemoteWorkerController::NoteDeadWorkerActor() {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mActor);
+
+  // The actor has been destroyed without a proper close() notification. Let's
+  // inform the observer.
+  if (mState == eReady) {
+    mObserver->Terminated();
+  }
+
+  mActor = nullptr;
+
+  Shutdown();
+}
+
 void RemoteWorkerController::CreationFailed() {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(mState == ePending || mState == eTerminated);
 
   if (mState == eTerminated) {
     MOZ_ASSERT(!mActor);
     MOZ_ASSERT(mPendingOps.IsEmpty());
     // Nothing to do.
     return;
   }
 
-  Shutdown();
+  NoteDeadWorker();
+
   mObserver->CreationFailed();
 }
 
 void RemoteWorkerController::CreationSucceeded() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(mState == ePending || mState == eTerminated);
 
   if (mState == eTerminated) {
     MOZ_ASSERT(!mActor);
     MOZ_ASSERT(mPendingOps.IsEmpty());
     // Nothing to do.
     return;
   }
 
   MOZ_ASSERT(mActor);
   mState = eReady;
 
   mObserver->CreationSucceeded();
 
-  for (UniquePtr<Op>& op : mPendingOps) {
-    switch (op->mType) {
-      case Op::eTerminate:
-        Terminate();
-        break;
-
-      case Op::eSuspend:
-        Suspend();
-        break;
-
-      case Op::eResume:
-        Resume();
-        break;
-
-      case Op::eFreeze:
-        Freeze();
-        break;
+  auto pendingOps = std::move(mPendingOps);
 
-      case Op::eThaw:
-        Thaw();
-        break;
-
-      case Op::ePortIdentifier:
-        AddPortIdentifier(op->mPortIdentifier);
-        break;
-
-      case Op::eAddWindowID:
-        AddWindowID(op->mWindowID);
-        break;
-
-      case Op::eRemoveWindowID:
-        RemoveWindowID(op->mWindowID);
-        break;
-
-      default:
-        MOZ_CRASH("Unknown op.");
-    }
-
-    op->Completed();
+  for (auto& op : pendingOps) {
+    DebugOnly<bool> started = op->MaybeStart(this);
+    MOZ_ASSERT(started);
   }
-
-  mPendingOps.Clear();
 }
 
 void RemoteWorkerController::ErrorPropagation(const ErrorValue& aValue) {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
   mObserver->ErrorReceived(aValue);
 }
 
 void RemoteWorkerController::WorkerTerminated() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
-  MOZ_ASSERT(mState == eReady);
+
+  NoteDeadWorker();
 
   mObserver->Terminated();
-  Shutdown();
+}
+
+void RemoteWorkerController::CancelAllPendingOps() {
+  AssertIsOnBackgroundThread();
+
+  auto pendingOps = std::move(mPendingOps);
+
+  for (auto& op : pendingOps) {
+    op->Cancel();
+  }
 }
 
 void RemoteWorkerController::Shutdown() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
-  MOZ_ASSERT(mState == ePending || mState == eReady);
+  Unused << NS_WARN_IF(mIsServiceWorker && !mPendingOps.IsEmpty());
+
+  if (mState == eTerminated) {
+    MOZ_ASSERT(mPendingOps.IsEmpty());
+    return;
+  }
 
   mState = eTerminated;
 
-  mPendingOps.Clear();
+  CancelAllPendingOps();
+
+  if (!mActor) {
+    return;
+  }
+
+  mActor->SetController(nullptr);
+
+  /**
+   * The "non-remote-side" of the Service Worker will have ensured that the
+   * remote worker is terminated before calling `Shutdown().`
+   */
+  if (mIsServiceWorker) {
+    mActor->MaybeSendDelete();
+  } else {
+    Unused << mActor->SendExecOp(RemoteWorkerTerminateOp());
+  }
+
+  mActor = nullptr;
+}
 
-  if (mActor) {
-    mActor->SetController(nullptr);
-    Unused << mActor->SendExecOp(RemoteWorkerTerminateOp());
-    mActor = nullptr;
+void RemoteWorkerController::NoteDeadWorker() {
+  AssertIsOnBackgroundThread();
+
+  CancelAllPendingOps();
+
+  /**
+   * The "non-remote-side" of the Service Worker will initiate `Shutdown()`
+   * once it's notified that all dispatched operations have either completed
+   * or canceled. That is, it'll explicitly call `Shutdown()` later.
+   */
+  if (!mIsServiceWorker) {
+    Shutdown();
+  }
+}
+
+template <typename... Args>
+void RemoteWorkerController::MaybeStartSharedWorkerOp(Args&&... aArgs) {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mIsServiceWorker);
+
+  UniquePtr<PendingSharedWorkerOp> op =
+      MakeUnique<PendingSharedWorkerOp>(std::forward<Args>(aArgs)...);
+
+  if (!op->MaybeStart(this)) {
+    mPendingOps.AppendElement(std::move(op));
   }
 }
 
 void RemoteWorkerController::AddWindowID(uint64_t aWindowID) {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aWindowID);
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(Op::eAddWindowID, aWindowID));
-    return;
-  }
-
-  if (mState == eTerminated) {
-    return;
-  }
-
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerAddWindowIDOp(aWindowID));
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eAddWindowID, aWindowID);
 }
 
 void RemoteWorkerController::RemoveWindowID(uint64_t aWindowID) {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(aWindowID);
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(Op::eRemoveWindowID, aWindowID));
-    return;
-  }
-
-  if (mState == eTerminated) {
-    return;
-  }
-
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerRemoveWindowIDOp(aWindowID));
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eRemoveWindowID, aWindowID);
 }
 
 void RemoteWorkerController::AddPortIdentifier(
     const MessagePortIdentifier& aPortIdentifier) {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(aPortIdentifier));
-    return;
-  }
-
-  if (mState == eTerminated) {
-    return;
-  }
-
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerPortIdentifierOp(aPortIdentifier));
-}
-
-void RemoteWorkerController::ForgetActorAndTerminate() {
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
-
-  // The actor has been destroyed without a proper close() notification. Let's
-  // inform the observer.
-  if (mState == eReady) {
-    mObserver->Terminated();
-  }
-
-  mActor = nullptr;
-  Terminate();
+  MaybeStartSharedWorkerOp(aPortIdentifier);
 }
 
 void RemoteWorkerController::Terminate() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
-  if (mState == eTerminated) {
-    return;
-  }
-
-  Shutdown();
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eTerminate);
 }
 
 void RemoteWorkerController::Suspend() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(Op::eSuspend));
-    return;
-  }
-
-  if (mState == eTerminated) {
-    return;
-  }
-
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerSuspendOp());
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eSuspend);
 }
 
 void RemoteWorkerController::Resume() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(Op::eResume));
-    return;
-  }
-
-  if (mState == eTerminated) {
-    return;
-  }
-
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerResumeOp());
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eResume);
 }
 
 void RemoteWorkerController::Freeze() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(Op::eFreeze));
-    return;
-  }
-
-  if (mState == eTerminated) {
-    return;
-  }
-
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerFreezeOp());
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eFreeze);
 }
 
 void RemoteWorkerController::Thaw() {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(XRE_IsParentProcess());
+
+  MaybeStartSharedWorkerOp(PendingSharedWorkerOp::eThaw);
+}
+
+RemoteWorkerController::PendingSharedWorkerOp::PendingSharedWorkerOp(
+    Type aType, uint64_t aWindowID)
+    : mType(aType), mWindowID(aWindowID) {
+  AssertIsOnBackgroundThread();
+}
+
+RemoteWorkerController::PendingSharedWorkerOp::PendingSharedWorkerOp(
+    const MessagePortIdentifier& aPortIdentifier)
+    : mType(ePortIdentifier), mPortIdentifier(aPortIdentifier) {
+  AssertIsOnBackgroundThread();
+}
 
-  if (mState == ePending) {
-    mPendingOps.AppendElement(new Op(Op::eThaw));
-    return;
+RemoteWorkerController::PendingSharedWorkerOp::~PendingSharedWorkerOp() {
+  AssertIsOnBackgroundThread();
+  MOZ_DIAGNOSTIC_ASSERT(mCompleted);
+}
+
+bool RemoteWorkerController::PendingSharedWorkerOp::MaybeStart(
+    RemoteWorkerController* const aOwner) {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mCompleted);
+  MOZ_ASSERT(aOwner);
+
+  if (aOwner->mState == RemoteWorkerController::eTerminated) {
+    Cancel();
+    return true;
+  }
+
+  if (aOwner->mState == RemoteWorkerController::ePending &&
+      mType != eTerminate) {
+    return false;
   }
 
-  if (mState == eTerminated) {
-    return;
+  switch (mType) {
+    case eTerminate:
+      aOwner->Shutdown();
+      break;
+    case eSuspend:
+      Unused << aOwner->mActor->SendExecOp(RemoteWorkerSuspendOp());
+      break;
+    case eResume:
+      Unused << aOwner->mActor->SendExecOp(RemoteWorkerResumeOp());
+      break;
+    case eFreeze:
+      Unused << aOwner->mActor->SendExecOp(RemoteWorkerFreezeOp());
+      break;
+    case eThaw:
+      Unused << aOwner->mActor->SendExecOp(RemoteWorkerThawOp());
+      break;
+    case ePortIdentifier:
+      Unused << aOwner->mActor->SendExecOp(
+          RemoteWorkerPortIdentifierOp(mPortIdentifier));
+      break;
+    case eAddWindowID:
+      Unused << aOwner->mActor->SendExecOp(
+          RemoteWorkerAddWindowIDOp(mWindowID));
+      break;
+    case eRemoveWindowID:
+      Unused << aOwner->mActor->SendExecOp(
+          RemoteWorkerRemoveWindowIDOp(mWindowID));
+      break;
+    default:
+      MOZ_CRASH("Unknown op.");
   }
 
-  MOZ_ASSERT(mState == eReady);
-  Unused << mActor->SendExecOp(RemoteWorkerThawOp());
+  mCompleted = true;
+
+  return true;
 }
 
-RemoteWorkerController::Op::~Op() {
-  MOZ_COUNT_DTOR(Op);
+void RemoteWorkerController::PendingSharedWorkerOp::Cancel() {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mCompleted);
 
   // We don't want to leak the port if the operation has not been processed.
-  if (!mCompleted && mType == ePortIdentifier) {
+  if (mType == ePortIdentifier) {
     MessagePortParent::ForceClose(mPortIdentifier.uuid(),
                                   mPortIdentifier.destinationUuid(),
                                   mPortIdentifier.sequenceId());
   }
+
+  mCompleted = true;
+}
+
+RemoteWorkerController::PendingServiceWorkerOp::PendingServiceWorkerOp(
+    ServiceWorkerOpArgs&& aArgs,
+    RefPtr<ServiceWorkerOpPromise::Private> aPromise)
+    : mArgs(std::move(aArgs)), mPromise(std::move(aPromise)) {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mPromise);
+}
+
+RemoteWorkerController::PendingServiceWorkerOp::~PendingServiceWorkerOp() {
+  AssertIsOnBackgroundThread();
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+}
+
+bool RemoteWorkerController::PendingServiceWorkerOp::MaybeStart(
+    RemoteWorkerController* const aOwner) {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mPromise);
+  MOZ_ASSERT(aOwner);
+
+  if (NS_WARN_IF(aOwner->mState == RemoteWorkerController::eTerminated)) {
+    mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
+    mPromise = nullptr;
+    return true;
+  }
+
+  // The target content process must still be starting up.
+  if (!aOwner->mActor) {
+    return false;
+  }
+
+  /**
+   * Allow termination operations to pass through while pending because the
+   * remote Service Worker can be terminated while still starting up.
+   */
+  if (aOwner->mState == RemoteWorkerController::ePending &&
+      mArgs.type() !=
+          ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs) {
+    return false;
+  }
+
+  if (mArgs.type() == ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs) {
+    auto& args = mArgs.get_ServiceWorkerMessageEventOpArgs();
+
+    ServiceWorkerMessageEventOpArgs copyArgs;
+    copyArgs.clientInfoAndState() = std::move(args.clientInfoAndState());
+
+    RefPtr<ServiceWorkerCloneData> copyData = new ServiceWorkerCloneData();
+    copyData->StealFromClonedMessageDataForBackgroundParent(args.clonedData());
+
+    if (!copyData->BuildClonedMessageDataForBackgroundParent(
+            aOwner->mActor->Manager(), copyArgs.clonedData())) {
+      mPromise->Reject(NS_ERROR_DOM_DATA_CLONE_ERR, __func__);
+      mPromise = nullptr;
+      return true;
+    }
+
+    mArgs = std::move(copyArgs);
+  }
+
+  return true;
+}
+
+void RemoteWorkerController::PendingServiceWorkerOp::Cancel() {
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mPromise);
+
+  mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
+  mPromise = nullptr;
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/workers/remoteworkers/RemoteWorkerController.h
+++ b/dom/workers/remoteworkers/RemoteWorkerController.h
@@ -3,16 +3,23 @@
 /* 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_RemoteWorkerController_h
 #define mozilla_dom_RemoteWorkerController_h
 
 #include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/ServiceWorkerOpArgs.h"
+#include "mozilla/dom/ServiceWorkerOpPromise.h"
 
 namespace mozilla {
 namespace dom {
 
 /* Here's a graph about this remote workers are spawned.
  *
  *  _________________________________    |   ________________________________
  * |                                 |   |  |                                |
@@ -74,17 +81,16 @@ namespace dom {
  *    WorkerPrivate.
  * 8. The RemoteWorkerParent actor is passed to the RemoteWorkerController.
  * 9. RemoteWorkerController now is ready to continue and it called
  *    RemoteWorkerObserver to inform that the operation is completed.
  *    In case there were pending operations, they are now executed.
  */
 
 class ErrorValue;
-class MessagePortIdentifier;
 class RemoteWorkerControllerParent;
 class RemoteWorkerData;
 class RemoteWorkerManager;
 class RemoteWorkerParent;
 
 class RemoteWorkerObserver {
  public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
@@ -129,77 +135,117 @@ class RemoteWorkerController final {
  private:
   RemoteWorkerController(const RemoteWorkerData& aData,
                          RemoteWorkerObserver* aObserver);
 
   ~RemoteWorkerController();
 
   void SetWorkerActor(RemoteWorkerParent* aActor);
 
+  void NoteDeadWorkerActor();
+
   void ErrorPropagation(const ErrorValue& aValue);
 
   void WorkerTerminated();
 
-  void ForgetActorAndTerminate();
-
   void Shutdown();
 
   void CreationFailed();
 
   void CreationSucceeded();
 
+  void CancelAllPendingOps();
+
+  template <typename... Args>
+  void MaybeStartSharedWorkerOp(Args&&... aArgs);
+
+  void NoteDeadWorker();
+
   RefPtr<RemoteWorkerObserver> mObserver;
   RefPtr<RemoteWorkerParent> mActor;
 
   enum {
     ePending,
     eReady,
     eTerminated,
   } mState;
 
   const bool mIsServiceWorker;
 
-  struct Op {
+  /**
+   * `PendingOp` is responsible for encapsulating logic for starting and
+   * canceling pending remote worker operations, as this logic may vary
+   * depending on the type of the remote worker and the type of the operation.
+   */
+  class PendingOp {
+   public:
+    PendingOp() = default;
+
+    PendingOp(const PendingOp&) = delete;
+
+    PendingOp& operator=(const PendingOp&) = delete;
+
+    virtual ~PendingOp() = default;
+
+    /**
+     * Returns `true` if execution has started and `false` otherwise.
+     *
+     * Starting execution may depend the state of `aOwner.`
+     */
+    virtual bool MaybeStart(RemoteWorkerController* const aOwner) = 0;
+
+    virtual void Cancel() = 0;
+  };
+
+  class PendingSharedWorkerOp final : public PendingOp {
+   public:
     enum Type {
       eTerminate,
       eSuspend,
       eResume,
       eFreeze,
       eThaw,
       ePortIdentifier,
       eAddWindowID,
       eRemoveWindowID,
     };
 
-    explicit Op(Type aType, uint64_t aWindowID = 0)
-        : mType(aType), mWindowID(aWindowID), mCompleted(false) {
-      MOZ_COUNT_CTOR(Op);
-    }
+    explicit PendingSharedWorkerOp(Type aType, uint64_t aWindowID = 0);
 
-    explicit Op(const MessagePortIdentifier& aPortIdentifier)
-        : mType(ePortIdentifier),
-          mPortIdentifier(aPortIdentifier),
-          mCompleted(false) {
-      MOZ_COUNT_CTOR(Op);
-    }
+    explicit PendingSharedWorkerOp(
+        const MessagePortIdentifier& aPortIdentifier);
+
+    ~PendingSharedWorkerOp();
 
-    // This object cannot be copied.
-    Op(Op const&) = delete;
-    Op& operator=(Op const&) = delete;
+    bool MaybeStart(RemoteWorkerController* const aOwner) override;
 
-    ~Op();
+    void Cancel() override;
 
-    void Completed() { mCompleted = true; }
-
-    Type mType;
-
-    MessagePortIdentifier mPortIdentifier;
-    uint64_t mWindowID;
-    bool mCompleted;
+   private:
+    const Type mType;
+    const MessagePortIdentifier mPortIdentifier;
+    const uint64_t mWindowID = 0;
+    bool mCompleted = false;
   };
 
-  nsTArray<UniquePtr<Op>> mPendingOps;
+  class PendingServiceWorkerOp final : public PendingOp {
+   public:
+    PendingServiceWorkerOp(ServiceWorkerOpArgs&& aArgs,
+                           RefPtr<ServiceWorkerOpPromise::Private> aPromise);
+
+    ~PendingServiceWorkerOp();
+
+    bool MaybeStart(RemoteWorkerController* const aOwner) override;
+
+    void Cancel() override;
+
+   private:
+    ServiceWorkerOpArgs mArgs;
+    RefPtr<ServiceWorkerOpPromise::Private> mPromise;
+  };
+
+  nsTArray<UniquePtr<PendingOp>> mPendingOps;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_RemoteWorkerController_h
--- a/dom/workers/remoteworkers/RemoteWorkerParent.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.cpp
@@ -79,17 +79,17 @@ void RemoteWorkerParent::ActorDestroy(IP
         new UnregisterActorRunnable(parent.forget());
 
     nsCOMPtr<nsIEventTarget> target =
         SystemGroup::EventTargetFor(TaskCategory::Other);
     target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
   }
 
   if (mController) {
-    mController->ForgetActorAndTerminate();
+    mController->NoteDeadWorkerActor();
     mController = nullptr;
   }
 }
 
 IPCResult RemoteWorkerParent::RecvCreated(const bool& aStatus) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 
@@ -112,16 +112,27 @@ IPCResult RemoteWorkerParent::RecvError(
 
   if (mController) {
     mController->ErrorPropagation(aValue);
   }
 
   return IPC_OK();
 }
 
+void RemoteWorkerParent::MaybeSendDelete() {
+  if (mDeleteSent) {
+    return;
+  }
+
+  // For some reason, if the following two lines are swapped, ASan says there's
+  // a UAF...
+  mDeleteSent = true;
+  Unused << Send__delete__(this);
+}
+
 IPCResult RemoteWorkerParent::RecvClose() {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(XRE_IsParentProcess());
 
   if (mController) {
     mController->WorkerTerminated();
   }
 
--- a/dom/workers/remoteworkers/RemoteWorkerParent.h
+++ b/dom/workers/remoteworkers/RemoteWorkerParent.h
@@ -21,26 +21,29 @@ class RemoteWorkerParent final : public 
   NS_INLINE_DECL_REFCOUNTING(RemoteWorkerParent)
 
   RemoteWorkerParent();
 
   void Initialize();
 
   void SetController(RemoteWorkerController* aController);
 
+  void MaybeSendDelete();
+
  private:
   ~RemoteWorkerParent();
 
   void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
 
   mozilla::ipc::IPCResult RecvError(const ErrorValue& aValue);
 
   mozilla::ipc::IPCResult RecvClose();
 
   mozilla::ipc::IPCResult RecvCreated(const bool& aStatus);
 
+  bool mDeleteSent = false;
   RefPtr<RemoteWorkerController> mController;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_RemoteWorkerParent_h