Bug 1649477 - Part 2: Unify JSActor manager logic, r=kmag
☠☠ backed out by d890454d6684 ☠ ☠
authorNika Layzell <nika@thelayzells.com>
Mon, 06 Jul 2020 20:27:08 +0000
changeset 539228 5b217aa88289d7f6a9766cef24649e43470052ee
parent 539227 8959d02b840f97944c8383bcff1363431ef8e9fb
child 539229 eca6e9dce450e6e9f5f023dc8feadb31fa50192b
push id120999
push usernlayzell@mozilla.com
push dateTue, 07 Jul 2020 23:52:01 +0000
treeherderautoland@830aa93d2b0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1649477
milestone80.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 1649477 - Part 2: Unify JSActor manager logic, r=kmag Differential Revision: https://phabricator.services.mozilla.com/D82101
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/InProcessChild.h
dom/ipc/InProcessImpl.cpp
dom/ipc/InProcessParent.h
dom/ipc/ProcessActor.cpp
dom/ipc/ProcessActor.h
dom/ipc/WindowGlobalActor.cpp
dom/ipc/WindowGlobalActor.h
dom/ipc/WindowGlobalChild.cpp
dom/ipc/WindowGlobalChild.h
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
dom/ipc/jsactor/JSActor.cpp
dom/ipc/jsactor/JSActor.h
dom/ipc/jsactor/JSActorManager.cpp
dom/ipc/jsactor/JSActorManager.h
dom/ipc/jsactor/JSActorService.cpp
dom/ipc/jsactor/JSActorService.h
dom/ipc/jsactor/JSProcessActorChild.cpp
dom/ipc/jsactor/JSProcessActorChild.h
dom/ipc/jsactor/JSProcessActorParent.cpp
dom/ipc/jsactor/JSProcessActorParent.h
dom/ipc/jsactor/JSProcessActorProtocol.cpp
dom/ipc/jsactor/JSProcessActorProtocol.h
dom/ipc/jsactor/JSWindowActorChild.cpp
dom/ipc/jsactor/JSWindowActorChild.h
dom/ipc/jsactor/JSWindowActorParent.cpp
dom/ipc/jsactor/JSWindowActorParent.h
dom/ipc/jsactor/JSWindowActorProtocol.cpp
dom/ipc/jsactor/JSWindowActorProtocol.h
dom/ipc/jsactor/moz.build
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2130,23 +2130,17 @@ void ContentChild::ActorDestroy(ActorDes
 
 #ifndef NS_FREE_PERMANENT_DATA
   // In release builds, there's no point in the content process
   // going through the full XPCOM shutdown path, because it doesn't
   // keep persistent state.
   ProcessChild::QuickExit();
 #else
   // Destroy our JSProcessActors, and reject any pending queries.
-  nsRefPtrHashtable<nsCStringHashKey, JSProcessActorChild> processActors;
-  mProcessActors.SwapElements(processActors);
-  for (auto iter = processActors.Iter(); !iter.Done(); iter.Next()) {
-    iter.Data()->RejectPendingQueries();
-    iter.Data()->AfterDestroy();
-  }
-  mProcessActors.Clear();
+  JSActorDidDestroy();
 
 #  if defined(XP_WIN)
   RefPtr<DllServices> dllSvc(DllServices::Get());
   dllSvc->DisableFull();
 #  endif  // defined(XP_WIN)
 
   if (gFirstIdleTask) {
     gFirstIdleTask->Cancel();
@@ -4248,48 +4242,42 @@ mozilla::ipc::IPCResult ContentChild::Re
 
 NS_IMETHODIMP ContentChild::GetChildID(uint64_t* aOut) {
   *aOut = mID;
   return NS_OK;
 }
 
 NS_IMETHODIMP ContentChild::GetActor(const nsACString& aName,
                                      JSProcessActorChild** retval) {
-  if (!CanSend()) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
-  // Check if this actor has already been created, and return it if it has.
-  if (mProcessActors.Contains(aName)) {
-    RefPtr<JSProcessActorChild> actor(mProcessActors.Get(aName));
-    actor.forget(retval);
-    return NS_OK;
-  }
-
-  // Otherwise, we want to create a new instance of this actor.
-  JS::RootedObject obj(RootingCx());
-  ErrorResult result;
-  ConstructActor(aName, &obj, result);
-  if (result.Failed()) {
-    return result.StealNSResult();
-  }
-
-  // Unwrap our actor to a JSProcessActorChild object.
+  ErrorResult error;
+  RefPtr<JSProcessActorChild> actor =
+      JSActorManager::GetActor(aName, error).downcast<JSProcessActorChild>();
+  if (error.Failed()) {
+    return error.StealNSResult();
+  }
+  actor.forget(retval);
+  return NS_OK;
+}
+
+already_AddRefed<JSActor> ContentChild::InitJSActor(
+    JS::HandleObject aMaybeActor, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSProcessActorChild> actor;
-  nsresult rv = UNWRAP_OBJECT(JSProcessActorChild, &obj, actor);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (aMaybeActor.get()) {
+    aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  } else {
+    actor = new JSProcessActorChild();
   }
 
   MOZ_RELEASE_ASSERT(!actor->Manager(),
                      "mManager was already initialized once!");
   actor->Init(aName, this);
-  mProcessActors.Put(aName, RefPtr{actor});
-  actor.forget(retval);
-  return NS_OK;
+  return actor.forget();
 }
 
 IPCResult ContentChild::RecvRawMessage(const JSActorMessageMeta& aMeta,
                                        const ClonedMessageData& aData,
                                        const ClonedMessageData& aStack) {
   RefPtr<JSProcessActorChild> actor;
   GetActor(aMeta.actorName(), getter_AddRefs(actor));
   if (actor) {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -790,17 +790,20 @@ class ContentChild final : public PConte
   mozilla::ipc::IPCResult RecvStopLoad(
       const MaybeDiscarded<BrowsingContext>& aContext,
       const uint32_t aStopFlags);
 
   mozilla::ipc::IPCResult RecvRawMessage(const JSActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData,
                                          const ClonedMessageData& aStack);
 
-  JSActor::Type GetSide() override { return JSActor::Type::Child; }
+  already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                        const nsACString& aName,
+                                        ErrorResult& aRv) override;
+  mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
 
   mozilla::ipc::IPCResult RecvHistoryCommitLength(
       const MaybeDiscarded<BrowsingContext>& aContext, uint32_t aLength);
 
   mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
 
  private:
 #ifdef NIGHTLY_BUILD
@@ -890,21 +893,16 @@ class ContentChild final : public PConte
   // off-main-thread.
   mozilla::Atomic<uint32_t> mPendingInputEvents;
 #endif
 
   uint32_t mNetworkLinkType = 0;
 
   // See `BrowsingContext::mEpochs` for an explanation of this field.
   uint64_t mBrowsingContextFieldEpoch = 0;
-
-  nsRefPtrHashtable<nsCStringHashKey, JSProcessActorChild> mProcessActors;
-  ContentChild(const ContentChild&) = delete;
-
-  const ContentChild& operator=(const ContentChild&) = delete;
 };
 
 inline nsISupports* ToSupports(mozilla::dom::ContentChild* aContentChild) {
   return static_cast<nsIDOMProcessChild*>(aContentChild);
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1826,23 +1826,17 @@ void ContentParent::ActorDestroy(ActorDe
     gpu->RemoveListener(this);
   }
 
   RecvRemoveGeolocationListener();
 
   mConsoleService = nullptr;
 
   // Destroy our JSProcessActors, and reject any pending queries.
-  nsRefPtrHashtable<nsCStringHashKey, JSProcessActorParent> processActors;
-  mProcessActors.SwapElements(processActors);
-  for (auto iter = processActors.Iter(); !iter.Done(); iter.Next()) {
-    iter.Data()->RejectPendingQueries();
-    iter.Data()->AfterDestroy();
-  }
-  mProcessActors.Clear();
+  JSActorDidDestroy();
 
   if (obs) {
     RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 
     props->SetPropertyAsUint64(u"childID"_ns, mChildID);
 
     if (AbnormalShutdown == why) {
       Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "content"_ns,
@@ -6852,48 +6846,42 @@ IPCResult ContentParent::RecvRawMessage(
     stack.BorrowFromClonedMessageDataForParent(aStack);
     actor->ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
   }
   return IPC_OK();
 }
 
 NS_IMETHODIMP ContentParent::GetActor(const nsACString& aName,
                                       JSProcessActorParent** retval) {
-  if (!CanSend()) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
-  // Check if this actor has already been created, and return it if it has.
-  if (mProcessActors.Contains(aName)) {
-    RefPtr<JSProcessActorParent> actor(mProcessActors.Get(aName));
-    actor.forget(retval);
-    return NS_OK;
-  }
-
-  // Otherwise, we want to create a new instance of this actor.
-  JS::RootedObject obj(RootingCx());
-  ErrorResult result;
-  ConstructActor(aName, &obj, result);
-  if (result.Failed()) {
-    return result.StealNSResult();
-  }
-
-  // Unwrap our actor to a JSProcessActorParent object.
+  ErrorResult error;
+  RefPtr<JSProcessActorParent> actor =
+      JSActorManager::GetActor(aName, error).downcast<JSProcessActorParent>();
+  if (error.Failed()) {
+    return error.StealNSResult();
+  }
+  actor.forget(retval);
+  return NS_OK;
+}
+
+already_AddRefed<JSActor> ContentParent::InitJSActor(
+    JS::HandleObject aMaybeActor, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSProcessActorParent> actor;
-  nsresult rv = UNWRAP_OBJECT(JSProcessActorParent, &obj, actor);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (aMaybeActor.get()) {
+    aRv = UNWRAP_OBJECT(JSProcessActorParent, aMaybeActor.get(), actor);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  } else {
+    actor = new JSProcessActorParent();
   }
 
   MOZ_RELEASE_ASSERT(!actor->Manager(),
                      "mManager was already initialized once!");
   actor->Init(aName, this);
-  mProcessActors.Put(aName, RefPtr{actor});
-  actor.forget(retval);
-  return NS_OK;
+  return actor.forget();
 }
 
 IPCResult ContentParent::RecvFOGData(ByteBuf&& buf) {
 #ifdef MOZ_GLEAN
   glean::FOGData(std::move(buf));
 #endif
   return IPC_OK();
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1352,17 +1352,20 @@ class ContentParent final
   uint64_t GetBrowsingContextFieldEpoch() const {
     return mBrowsingContextFieldEpoch;
   }
 
   void UpdateNetworkLinkType();
 
   static bool ShouldSyncPreference(const char16_t* aData);
 
-  JSActor::Type GetSide() override { return JSActor::Type::Parent; }
+  already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                        const nsACString& aName,
+                                        ErrorResult& aRv) override;
+  mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
 
  private:
   // Return an existing ContentParent if possible. Otherwise, `nullptr`.
   static already_AddRefed<ContentParent> GetUsedBrowserProcess(
       const nsAString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
       uint32_t aMaxContentParents, bool aPreferUsed);
 
   void AddToPool(nsTArray<ContentParent*>&);
@@ -1525,18 +1528,16 @@ class ContentParent final
   nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> mGroups;
 
   // See `BrowsingContext::mEpochs` for an explanation of this field.
   uint64_t mBrowsingContextFieldEpoch = 0;
 
   // A preference serializer used to share preferences with the process.
   // Cleared once startup is complete.
   UniquePtr<mozilla::ipc::SharedPreferenceSerializer> mPrefSerializer;
-
-  nsRefPtrHashtable<nsCStringHashKey, JSProcessActorParent> mProcessActors;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ContentParent, NS_CONTENTPARENT_IID)
 
 // This is the C++ version of remoteTypePrefix in E10SUtils.jsm.
 const nsDependentSubstring RemoteTypePrefix(
     const nsAString& aContentProcessType);
 
--- a/dom/ipc/InProcessChild.h
+++ b/dom/ipc/InProcessChild.h
@@ -41,17 +41,22 @@ class InProcessChild final : public nsID
   static InProcessChild* Singleton();
 
   // Get the parent side of the in-process child actor |aActor|. If |aActor| is
   // not an in-process actor, or is not connected, this method will return
   // |nullptr|.
   static IProtocol* ParentActorFor(IProtocol* aActor);
 
   const nsAString& GetRemoteType() const override { return VoidString(); }
-  JSActor::Type GetSide() override { return JSActor::Type::Child; }
+
+ protected:
+  already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                        const nsACString& aName,
+                                        ErrorResult& aRv) override;
+  mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
 
  private:
   // NOTE: PInProcess lifecycle management is declared as staic methods and
   // state on InProcessParent, and implemented in InProcessImpl.cpp.
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   ~InProcessChild() = default;
 
   static StaticRefPtr<InProcessChild> sSingleton;
--- a/dom/ipc/InProcessImpl.cpp
+++ b/dom/ipc/InProcessImpl.cpp
@@ -106,20 +106,22 @@ NS_IMETHODIMP
 InProcessParent::Observe(nsISupports* aSubject, const char* aTopic,
                          const char16_t* aData) {
   MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
   InProcessParent::Shutdown();
   return NS_OK;
 }
 
 void InProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
+  JSActorDidDestroy();
   InProcessParent::Shutdown();
 }
 
 void InProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+  JSActorDidDestroy();
   InProcessParent::Shutdown();
 }
 
 /////////////////////////
 // nsIDOMProcessParent //
 /////////////////////////
 
 NS_IMETHODIMP
@@ -134,48 +136,42 @@ InProcessParent::GetOsPid(int32_t* aOsPi
   // so we can return the current process id.
   *aOsPid = base::GetCurrentProcId();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InProcessParent::GetActor(const nsACString& aName,
                           JSProcessActorParent** aActor) {
-  if (!CanSend()) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  ErrorResult error;
+  RefPtr<JSProcessActorParent> actor =
+      JSActorManager::GetActor(aName, error).downcast<JSProcessActorParent>();
+  if (error.Failed()) {
+    return error.StealNSResult();
   }
-
-  // Check if this actor has already been created, and return it if it has.
-  if (mProcessActors.Contains(aName)) {
-    RefPtr<JSProcessActorParent> actor(mProcessActors.Get(aName));
-    actor.forget(aActor);
-    return NS_OK;
-  }
+  actor.forget(aActor);
+  return NS_OK;
+}
 
-  // Otherwise, we want to create a new instance of this actor.
-  JS::RootedObject obj(RootingCx());
-  ErrorResult result;
-  ConstructActor(aName, &obj, result);
-  if (result.Failed()) {
-    return result.StealNSResult();
-  }
-
-  // Unwrap our actor to a JSProcessActorParent object.
+already_AddRefed<JSActor> InProcessParent::InitJSActor(
+    JS::HandleObject aMaybeActor, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSProcessActorParent> actor;
-  nsresult rv = UNWRAP_OBJECT(JSProcessActorParent, &obj, actor);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (aMaybeActor.get()) {
+    aRv = UNWRAP_OBJECT(JSProcessActorParent, aMaybeActor.get(), actor);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  } else {
+    actor = new JSProcessActorParent();
   }
 
   MOZ_RELEASE_ASSERT(!actor->Manager(),
                      "mManager was already initialized once!");
   actor->Init(aName, this);
-  mProcessActors.Put(aName, RefPtr{actor});
-  actor.forget(aActor);
-  return NS_OK;
+  return actor.forget();
 }
 
 NS_IMETHODIMP
 InProcessParent::GetCanSend(bool* aCanSend) {
   *aCanSend = CanSend();
   return NS_OK;
 }
 
@@ -189,48 +185,42 @@ NS_IMETHODIMP
 InProcessChild::GetChildID(uint64_t* aChildID) {
   *aChildID = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InProcessChild::GetActor(const nsACString& aName,
                          JSProcessActorChild** aActor) {
-  if (!CanSend()) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  ErrorResult error;
+  RefPtr<JSProcessActorChild> actor =
+      JSActorManager::GetActor(aName, error).downcast<JSProcessActorChild>();
+  if (error.Failed()) {
+    return error.StealNSResult();
   }
-
-  // Check if this actor has already been created, and return it if it has.
-  if (mProcessActors.Contains(aName)) {
-    RefPtr<JSProcessActorChild> actor(mProcessActors.Get(aName));
-    actor.forget(aActor);
-    return NS_OK;
-  }
+  actor.forget(aActor);
+  return NS_OK;
+}
 
-  // Otherwise, we want to create a new instance of this actor.
-  JS::RootedObject obj(RootingCx());
-  ErrorResult result;
-  ConstructActor(aName, &obj, result);
-  if (result.Failed()) {
-    return result.StealNSResult();
-  }
-
-  // Unwrap our actor to a JSProcessActorChild object.
+already_AddRefed<JSActor> InProcessChild::InitJSActor(
+    JS::HandleObject aMaybeActor, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSProcessActorChild> actor;
-  nsresult rv = UNWRAP_OBJECT(JSProcessActorChild, &obj, actor);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (aMaybeActor.get()) {
+    aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  } else {
+    actor = new JSProcessActorChild();
   }
 
   MOZ_RELEASE_ASSERT(!actor->Manager(),
                      "mManager was already initialized once!");
   actor->Init(aName, this);
-  mProcessActors.Put(aName, RefPtr{actor});
-  actor.forget(aActor);
-  return NS_OK;
+  return actor.forget();
 }
 
 NS_IMETHODIMP
 InProcessChild::GetCanSend(bool* aCanSend) {
   *aCanSend = CanSend();
   return NS_OK;
 }
 
--- a/dom/ipc/InProcessParent.h
+++ b/dom/ipc/InProcessParent.h
@@ -43,17 +43,22 @@ class InProcessParent final : public nsI
   static InProcessParent* Singleton();
 
   // Get the child side of the in-process child actor |aActor|. If |aActor| is
   // not an in-process actor, or is not connected, this method will return
   // |nullptr|.
   static IProtocol* ChildActorFor(IProtocol* aActor);
 
   const nsAString& GetRemoteType() const override { return VoidString(); };
-  JSActor::Type GetSide() override { return JSActor::Type::Parent; }
+
+ protected:
+  already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                        const nsACString& aName,
+                                        ErrorResult& aRv) override;
+  mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
 
  private:
   // Lifecycle management is implemented in InProcessImpl.cpp
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   ~InProcessParent() = default;
 
   static void Startup();
   static void Shutdown();
--- a/dom/ipc/ProcessActor.cpp
+++ b/dom/ipc/ProcessActor.cpp
@@ -8,115 +8,32 @@
 
 #include "nsContentUtils.h"
 #include "mozJSComponentLoader.h"
 #include "mozilla/ContentBlockingAllowList.h"
 #include "mozilla/Logging.h"
 #include "mozilla/dom/JSActorService.h"
 #include "mozilla/dom/JSProcessActorParent.h"
 #include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/JSProcessActorProtocol.h"
 
 namespace mozilla {
 namespace dom {
 
-void ProcessActor::ConstructActor(const nsACString& aName,
-                                  JS::MutableHandleObject aActor,
-                                  ErrorResult& aRv) {
-  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-
-  JSActor::Type actorType = GetSide();
-  MOZ_ASSERT_IF(actorType == JSActor::Type::Parent, XRE_IsParentProcess());
-
-  // Constructing an actor requires a running script, so push an AutoEntryScript
-  // onto the stack.
-  AutoEntryScript aes(xpc::PrivilegedJunkScope(), "ProcessActor construction");
-  JSContext* cx = aes.cx();
-
-  RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
-  if (!actorSvc) {
-    aRv.ThrowInvalidStateError(
-        "While constructing JSProcessActor, could not acquire JSActorService.");
-    return;
-  }
-
+already_AddRefed<JSActorProtocol> ProcessActor::MatchingJSActorProtocol(
+    JSActorService* aActorSvc, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSProcessActorProtocol> proto =
-      actorSvc->GetJSProcessActorProtocol(aName);
+      aActorSvc->GetJSProcessActorProtocol(aName);
   if (!proto) {
     aRv.ThrowNotFoundError(nsPrintfCString("No such JSProcessActor '%s'",
                                            PromiseFlatCString(aName).get()));
-    return;
+    return nullptr;
   }
 
   if (!proto->Matches(GetRemoteType())) {
-    aRv.ThrowTypeMismatchError(
-        nsPrintfCString("JSProcessActor '%s' does not match this process",
-                        PromiseFlatCString(aName).get()));
-    return;
-  }
-
-  // Load the module using mozJSComponentLoader.
-  RefPtr<mozJSComponentLoader> loader = mozJSComponentLoader::Get();
-  MOZ_ASSERT(loader);
-
-  JS::RootedObject global(cx);
-  JS::RootedObject exports(cx);
-
-  const JSProcessActorProtocol::Sided* side;
-  if (actorType == JSActor::Type::Parent) {
-    side = &proto->Parent();
-  } else {
-    side = &proto->Child();
+    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+    return nullptr;
   }
-
-  // Support basic functionally such as SendAsyncMessage and SendQuery for
-  // unspecified moduleURI.
-  if (!side->mModuleURI) {
-    RefPtr<JSActor> actor;
-    if (actorType == JSActor::Type::Parent) {
-      actor = new JSProcessActorParent();
-    } else {
-      actor = new JSProcessActorChild();
-    }
-
-    JS::Rooted<JS::Value> wrapper(cx);
-    if (!ToJSValue(cx, actor, &wrapper)) {
-      aRv.NoteJSContextException(cx);
-      return;
-    }
-
-    MOZ_ASSERT(wrapper.isObject());
-    aActor.set(&wrapper.toObject());
-    return;
-  }
-
-  aRv = loader->Import(cx, side->mModuleURI.ref(), &global, &exports);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  MOZ_ASSERT(exports, "null exports!");
-
-  // Load the specific property from our module.
-  JS::RootedValue ctor(cx);
-  nsAutoCString ctorName(aName);
-  ctorName.Append(actorType == JSActor::Type::Parent ? "Parent"_ns
-                                                     : "Child"_ns);
-  if (!JS_GetProperty(cx, exports, ctorName.get(), &ctor)) {
-    aRv.NoteJSContextException(cx);
-    return;
-  }
-
-  if (NS_WARN_IF(!ctor.isObject())) {
-    nsPrintfCString message("Could not find actor constructor '%s'",
-                            PromiseFlatCString(ctorName).get());
-    aRv.ThrowNotFoundError(message);
-    return;
-  }
-
-  // Invoke the constructor loaded from the module.
-  if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), aActor)) {
-    aRv.NoteJSContextException(cx);
-    return;
-  }
+  return proto.forget();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/ProcessActor.h
+++ b/dom/ipc/ProcessActor.h
@@ -9,30 +9,29 @@
 
 #include "nsWrapperCache.h"
 #include "nsISupports.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIURI.h"
 #include "nsString.h"
 #include "mozilla/dom/JSActor.h"
+#include "mozilla/dom/JSActorManager.h"
 
 namespace mozilla {
 namespace dom {
 
-// Common base class for Content{Parent, Child}.
-class ProcessActor : public nsISupports {
+// Common base class for Content{Parent, Child} and InProcess{Parent, Child}.
+class ProcessActor : public JSActorManager {
  protected:
   virtual ~ProcessActor() = default;
 
-  // Load the module for the named Content Actor and contruct it.
-  // This method will not initialize the actor or set its manager,
-  // which is handled by callers.
-  void ConstructActor(const nsACString& aName, JS::MutableHandleObject aActor,
-                      ErrorResult& aRv);
+  already_AddRefed<JSActorProtocol> MatchingJSActorProtocol(
+      JSActorService* aActorSvc, const nsACString& aName,
+      ErrorResult& aRv) final;
+
   virtual const nsAString& GetRemoteType() const = 0;
-  virtual JSActor::Type GetSide() = 0;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ProcessActor_h
--- a/dom/ipc/WindowGlobalActor.cpp
+++ b/dom/ipc/WindowGlobalActor.cpp
@@ -8,16 +8,17 @@
 
 #include "nsContentUtils.h"
 #include "mozJSComponentLoader.h"
 #include "mozilla/ContentBlockingAllowList.h"
 #include "mozilla/Logging.h"
 #include "mozilla/dom/JSActorService.h"
 #include "mozilla/dom/JSWindowActorParent.h"
 #include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSWindowActorProtocol.h"
 #include "mozilla/net/CookieJarSettings.h"
 
 namespace mozilla {
 namespace dom {
 
 // CORPP 3.1.3 https://mikewest.github.io/corpp/#integration-html
 static nsILoadInfo::CrossOriginEmbedderPolicy InheritedPolicy(
     dom::BrowsingContext* aBrowsingContext) {
@@ -130,110 +131,27 @@ WindowGlobalInit WindowGlobalActor::Wind
 
   // Most data here is specific to the Document, which can change without
   // creating a new WindowGlobal. Anything new added here which fits that
   // description should also be synchronized in
   // WindowGlobalChild::OnNewDocument.
   return init;
 }
 
-void WindowGlobalActor::ConstructActor(const nsACString& aName,
-                                       JS::MutableHandleObject aActor,
-                                       ErrorResult& aRv) {
-  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-
-  JSActor::Type actorType = GetSide();
-  MOZ_ASSERT_IF(actorType == JSActor::Type::Parent, XRE_IsParentProcess());
-
-  // Constructing an actor requires a running script, so push an AutoEntryScript
-  // onto the stack.
-  AutoEntryScript aes(xpc::PrivilegedJunkScope(),
-                      "WindowGlobalActor construction");
-  JSContext* cx = aes.cx();
-
-  RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
-  if (!actorSvc) {
-    aRv.ThrowNotSupportedError("Could not acquire actor service");
-    return;
-  }
-
+already_AddRefed<JSActorProtocol> WindowGlobalActor::MatchingJSActorProtocol(
+    JSActorService* aActorSvc, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSWindowActorProtocol> proto =
-      actorSvc->GetJSWindowActorProtocol(aName);
+      aActorSvc->GetJSWindowActorProtocol(aName);
   if (!proto) {
-    aRv.ThrowNotSupportedError(nsPrintfCString(
-        "Could not get JSWindowActorProtocol: %s is not registered",
-        PromiseFlatCString(aName).get()));
-    return;
+    aRv.ThrowNotFoundError(nsPrintfCString("No such JSWindowActor '%s'",
+                                           PromiseFlatCString(aName).get()));
+    return nullptr;
   }
 
   if (!proto->Matches(BrowsingContext(), GetDocumentURI(), GetRemoteType())) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return;
-  }
-
-  // Load the module using mozJSComponentLoader.
-  RefPtr<mozJSComponentLoader> loader = mozJSComponentLoader::Get();
-  MOZ_ASSERT(loader);
-
-  JS::RootedObject global(cx);
-  JS::RootedObject exports(cx);
-
-  const JSWindowActorProtocol::Sided* side;
-  if (actorType == JSActor::Type::Parent) {
-    side = &proto->Parent();
-  } else {
-    side = &proto->Child();
+    return nullptr;
   }
-
-  // Support basic functionally such as SendAsyncMessage and SendQuery for
-  // unspecified moduleURI.
-  if (!side->mModuleURI) {
-    RefPtr<JSActor> actor;
-    if (actorType == JSActor::Type::Parent) {
-      actor = new JSWindowActorParent();
-    } else {
-      actor = new JSWindowActorChild();
-    }
-
-    JS::Rooted<JS::Value> wrapper(cx);
-    if (!ToJSValue(cx, actor, &wrapper)) {
-      aRv.NoteJSContextException(cx);
-      return;
-    }
-
-    MOZ_ASSERT(wrapper.isObject());
-    aActor.set(&wrapper.toObject());
-    return;
-  }
-
-  aRv = loader->Import(cx, side->mModuleURI.ref(), &global, &exports);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  MOZ_ASSERT(exports, "null exports!");
-
-  // Load the specific property from our module.
-  JS::RootedValue ctor(cx);
-  nsAutoCString ctorName(aName);
-  ctorName.Append(actorType == JSActor::Type::Parent ? "Parent"_ns
-                                                     : "Child"_ns);
-  if (!JS_GetProperty(cx, exports, ctorName.get(), &ctor)) {
-    aRv.NoteJSContextException(cx);
-    return;
-  }
-
-  if (NS_WARN_IF(!ctor.isObject())) {
-    nsPrintfCString message("Could not find actor constructor '%s'",
-                            ctorName.get());
-    aRv.ThrowNotFoundError(message);
-    return;
-  }
-
-  // Invoke the constructor loaded from the module.
-  if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), aActor)) {
-    aRv.NoteJSContextException(cx);
-    return;
-  }
+  return proto.forget();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/WindowGlobalActor.h
+++ b/dom/ipc/WindowGlobalActor.h
@@ -9,44 +9,43 @@
 
 #include "nsWrapperCache.h"
 #include "nsISupports.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIURI.h"
 #include "nsString.h"
 #include "mozilla/dom/JSActor.h"
+#include "mozilla/dom/JSActorManager.h"
 #include "mozilla/dom/WindowGlobalTypes.h"
 
 namespace mozilla {
 namespace dom {
 
 // Common base class for WindowGlobal{Parent, Child}.
-class WindowGlobalActor : public nsISupports {
+class WindowGlobalActor : public JSActorManager {
  public:
   // Called to determine initial state for a window global actor created for an
   // initial about:blank document.
   static WindowGlobalInit AboutBlankInitializer(
       dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal);
 
   // Called to determine initial state for a window global actor created for a
   // specific existing nsGlobalWindowInner.
   static WindowGlobalInit WindowInitializer(nsGlobalWindowInner* aWindow);
 
  protected:
   virtual ~WindowGlobalActor() = default;
 
-  // Load the module for the named Window Actor and contruct it.
-  // This method will not initialize the actor or set its manager,
-  // which is handled by callers.
-  void ConstructActor(const nsACString& aName, JS::MutableHandleObject aActor,
-                      ErrorResult& aRv);
+  already_AddRefed<JSActorProtocol> MatchingJSActorProtocol(
+      JSActorService* aActorSvc, const nsACString& aName,
+      ErrorResult& aRv) final;
+
   virtual nsIURI* GetDocumentURI() = 0;
   virtual const nsAString& GetRemoteType() = 0;
-  virtual JSActor::Type GetSide() = 0;
   virtual dom::BrowsingContext* BrowsingContext() = 0;
 
   static WindowGlobalInit BaseInitializer(
       dom::BrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
       uint64_t aOuterWindowId);
 };
 
 }  // namespace dom
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -315,26 +315,17 @@ void WindowGlobalChild::BeforeUnloadRemo
 
 void WindowGlobalChild::Destroy() {
   // Destroying a WindowGlobalChild requires running script, so hold off on
   // doing it until we can safely run JS callbacks.
   nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
       "WindowGlobalChild::Destroy", [self = RefPtr<WindowGlobalChild>(this)]() {
         // Make a copy so that we can avoid potential iterator invalidation when
         // calling the user-provided Destroy() methods.
-        nsTArray<RefPtr<JSWindowActorChild>> windowActors(
-            self->mWindowActors.Count());
-        for (auto iter = self->mWindowActors.Iter(); !iter.Done();
-             iter.Next()) {
-          windowActors.AppendElement(iter.UserData());
-        }
-
-        for (auto& windowActor : windowActors) {
-          windowActor->StartDestroy();
-        }
+        self->JSActorWillDestroy();
 
         // Perform async IPC shutdown unless we're not in-process, and our
         // BrowserChild is in the process of being destroyed, which will destroy
         // us as well.
         RefPtr<BrowserChild> browserChild = self->GetBrowserChild();
         if (!browserChild || !browserChild->IsDestroyed()) {
           self->SendDestroy();
         }
@@ -597,83 +588,66 @@ const nsAString& WindowGlobalChild::GetR
     return ContentChild::GetSingleton()->GetRemoteType();
   }
 
   return VoidString();
 }
 
 already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
     const nsACString& aName, ErrorResult& aRv) {
-  if (!CanSend()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  // Check if this actor has already been created, and return it if it has.
-  if (mWindowActors.Contains(aName)) {
-    return do_AddRef(mWindowActors.GetWeak(aName));
-  }
+  return JSActorManager::GetActor(aName, aRv).downcast<JSWindowActorChild>();
+}
 
-  // Otherwise, we want to create a new instance of this actor.
-  JS::RootedObject obj(RootingCx());
-  ConstructActor(aName, &obj, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  // Unwrap our actor to a JSWindowActorChild object.
+already_AddRefed<JSActor> WindowGlobalChild::InitJSActor(
+    JS::HandleObject aMaybeActor, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSWindowActorChild> actor;
-  if (NS_FAILED(UNWRAP_OBJECT(JSWindowActorChild, &obj, actor))) {
-    return nullptr;
+  if (aMaybeActor.get()) {
+    aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  } else {
+    actor = new JSWindowActorChild();
   }
 
   MOZ_RELEASE_ASSERT(!actor->GetManager(),
                      "mManager was already initialized once!");
   actor->Init(aName, this);
-  mWindowActors.Put(aName, RefPtr{actor});
   return actor.forget();
 }
 
 void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
              "Destroying WindowGlobalChild can run script");
 
   gWindowGlobalChildById->Remove(InnerWindowId());
 
 #ifdef MOZ_GECKO_PROFILER
   profiler_unregister_page(InnerWindowId());
 #endif
 
   // Destroy our JSActors, and reject any pending queries.
-  nsRefPtrHashtable<nsCStringHashKey, JSWindowActorChild> windowActors;
-  mWindowActors.SwapElements(windowActors);
-  for (auto iter = windowActors.Iter(); !iter.Done(); iter.Next()) {
-    iter.Data()->RejectPendingQueries();
-    iter.Data()->AfterDestroy();
-  }
-  windowActors.Clear();
+  JSActorDidDestroy();
 }
 
 WindowGlobalChild::~WindowGlobalChild() {
   MOZ_ASSERT(!gWindowGlobalChildById ||
              !gWindowGlobalChildById->Contains(InnerWindowId()));
-  MOZ_ASSERT(!mWindowActors.Count());
 }
 
 JSObject* WindowGlobalChild::WrapObject(JSContext* aCx,
                                         JS::Handle<JSObject*> aGivenProto) {
   return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 nsISupports* WindowGlobalChild::GetParentObject() {
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild, mWindowGlobal,
-                                      mWindowActors)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild, mWindowGlobal)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -111,17 +111,21 @@ class WindowGlobalChild final : public W
   void OnNewDocument(Document* aNewDocument);
 
   nsISupports* GetParentObject();
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
  protected:
   const nsAString& GetRemoteType() override;
-  JSActor::Type GetSide() override { return JSActor::Type::Child; }
+
+  already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                        const nsACString& aName,
+                                        ErrorResult& aRv) override;
+  mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
 
   // IPC messages
   mozilla::ipc::IPCResult RecvRawMessage(const JSActorMessageMeta& aMeta,
                                          const ClonedMessageData& aData,
                                          const ClonedMessageData& aStack);
 
   mozilla::ipc::IPCResult RecvMakeFrameLocal(
       const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
@@ -154,17 +158,16 @@ class WindowGlobalChild final : public W
  private:
   WindowGlobalChild(dom::WindowContext* aWindowContext,
                     nsIPrincipal* aPrincipal, nsIURI* aURI);
 
   ~WindowGlobalChild();
 
   RefPtr<nsGlobalWindowInner> mWindowGlobal;
   RefPtr<dom::WindowContext> mWindowContext;
-  nsRefPtrHashtable<nsCStringHashKey, JSWindowActorChild> mWindowActors;
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIURI> mDocumentURI;
   int64_t mBeforeUnloadListeners = 0;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -398,24 +398,17 @@ mozilla::ipc::IPCResult WindowGlobalPare
     const IPCClientInfo& aIPCClientInfo) {
   mClientInfo = Some(ClientInfo(aIPCClientInfo));
   return IPC_OK();
 }
 
 IPCResult WindowGlobalParent::RecvDestroy() {
   // Make a copy so that we can avoid potential iterator invalidation when
   // calling the user-provided Destroy() methods.
-  nsTArray<RefPtr<JSWindowActorParent>> windowActors(mWindowActors.Count());
-  for (auto iter = mWindowActors.Iter(); !iter.Done(); iter.Next()) {
-    windowActors.AppendElement(iter.UserData());
-  }
-
-  for (auto& windowActor : windowActors) {
-    windowActor->StartDestroy();
-  }
+  JSActorWillDestroy();
 
   if (CanSend()) {
     RefPtr<BrowserParent> browserParent = GetBrowserParent();
     if (!browserParent || !browserParent->IsDestroyed()) {
       Unused << Send__delete__(this);
     }
   }
   return IPC_OK();
@@ -487,43 +480,34 @@ void WindowGlobalParent::NotifyContentBl
         new RemoteWebProgress(0, false, BrowsingContext()->IsTopContent());
     GetBrowsingContext()->Top()->GetWebProgress()->OnContentBlockingEvent(
         webProgress, aRequest, event.value());
   }
 }
 
 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
     const nsACString& aName, ErrorResult& aRv) {
-  if (!CanSend()) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  // Check if this actor has already been created, and return it if it has.
-  if (mWindowActors.Contains(aName)) {
-    return do_AddRef(mWindowActors.GetWeak(aName));
-  }
+  return JSActorManager::GetActor(aName, aRv).downcast<JSWindowActorParent>();
+}
 
-  // Otherwise, we want to create a new instance of this actor.
-  JS::RootedObject obj(RootingCx());
-  ConstructActor(aName, &obj, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-
-  // Unwrap our actor to a JSWindowActorParent object.
+already_AddRefed<JSActor> WindowGlobalParent::InitJSActor(
+    JS::HandleObject aMaybeActor, const nsACString& aName, ErrorResult& aRv) {
   RefPtr<JSWindowActorParent> actor;
-  if (NS_FAILED(UNWRAP_OBJECT(JSWindowActorParent, &obj, actor))) {
-    return nullptr;
+  if (aMaybeActor.get()) {
+    aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+  } else {
+    actor = new JSWindowActorParent();
   }
 
   MOZ_RELEASE_ASSERT(!actor->GetManager(),
                      "mManager was already initialized once!");
   actor->Init(aName, this);
-  mWindowActors.Put(aName, RefPtr{actor});
   return actor.forget();
 }
 
 bool WindowGlobalParent::IsCurrentGlobal() {
   return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
 }
 
 namespace {
@@ -794,37 +778,29 @@ void WindowGlobalParent::ActorDestroy(Ac
                              net::SchemeIsHTTPS(mDocumentURI))) {
           GetContentBlockingLog()->ReportOrigins();
         }
       }
     }
   }
 
   // Destroy our JSWindowActors, and reject any pending queries.
-  nsRefPtrHashtable<nsCStringHashKey, JSWindowActorParent> windowActors;
-  mWindowActors.SwapElements(windowActors);
-  for (auto iter = windowActors.Iter(); !iter.Done(); iter.Next()) {
-    iter.Data()->RejectPendingQueries();
-    iter.Data()->AfterDestroy();
-  }
-  windowActors.Clear();
+  JSActorDidDestroy();
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(ToSupports(this), "window-global-destroyed", nullptr);
   }
 
   if (mOriginCounter) {
     mOriginCounter->Accumulate();
   }
 }
 
-WindowGlobalParent::~WindowGlobalParent() {
-  MOZ_ASSERT(!mWindowActors.Count());
-}
+WindowGlobalParent::~WindowGlobalParent() = default;
 
 JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
                                          JS::Handle<JSObject*> aGivenProto) {
   return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 nsIGlobalObject* WindowGlobalParent::GetParentObject() {
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
@@ -877,18 +853,17 @@ void WindowGlobalParent::AddMixedContent
 
   mMixedContentSecurityState |= aStateFlags;
 
   if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
     GetBrowsingContext()->UpdateSecurityStateForLocationOrMixedContentChange();
   }
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent, WindowContext,
-                                   mWindowActors)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent, WindowContext)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
                                                WindowContext)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent)
 NS_INTERFACE_MAP_END_INHERITING(WindowContext)
 
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -196,17 +196,20 @@ class WindowGlobalParent final : public 
   void AddMixedContentSecurityState(uint32_t aStateFlags);
   uint32_t GetMixedContentSecurityFlags() { return mMixedContentSecurityState; }
 
   nsITransportSecurityInfo* GetSecurityInfo() { return mSecurityInfo; }
 
   const nsAString& GetRemoteType() override;
 
  protected:
-  JSActor::Type GetSide() override { return JSActor::Type::Parent; }
+  already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                        const nsACString& aName,
+                                        ErrorResult& aRv) override;
+  mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
 
   // IPC messages
   mozilla::ipc::IPCResult RecvLoadURI(
       const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
       nsDocShellLoadState* aLoadState, bool aSetNavigating);
   mozilla::ipc::IPCResult RecvInternalLoad(
       const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
       nsDocShellLoadState* aLoadState);
@@ -261,17 +264,16 @@ class WindowGlobalParent final : public 
 
   // NOTE: This document principal doesn't reflect possible |document.domain|
   // mutations which may have been made in the actual document.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIPrincipal> mDocContentBlockingAllowListPrincipal;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsString mDocumentTitle;
 
-  nsRefPtrHashtable<nsCStringHashKey, JSWindowActorParent> mWindowActors;
   bool mIsInitialDocument;
 
   // True if this window has a "beforeunload" event listener.
   bool mHasBeforeUnload;
 
   // The log of all content blocking actions taken on the document related to
   // this WindowGlobalParent. This is only stored on top-level documents and
   // includes the activity log for all of the nested subdocuments as well.
--- a/dom/ipc/jsactor/JSActor.cpp
+++ b/dom/ipc/jsactor/JSActor.cpp
@@ -30,30 +30,30 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSActor)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSActor)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(JSActor)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSActor)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWrappedJS)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingQueries)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSActor)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWrappedJS)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingQueries)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(JSActor)
 
-JSActor::JSActor() : mNextQueryId(0) {}
-
 // RAII class to ensure that, if we crash while handling a message, the
 // crash will be annotated with the name of the actor and the message.
 struct MOZ_RAII AutoAnnotateMessageInCaseOfCrash {
   AutoAnnotateMessageInCaseOfCrash(const nsCString& aActorName,
                                    const nsCString& aMessageName) {
     CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::JSActorName,
                                        aActorName);
     CrashReporter::AnnotateCrashReport(
@@ -62,28 +62,45 @@ struct MOZ_RAII AutoAnnotateMessageInCas
   ~AutoAnnotateMessageInCaseOfCrash() {
     CrashReporter::RemoveCrashReportAnnotation(
         CrashReporter::Annotation::JSActorMessage);
     CrashReporter::RemoveCrashReportAnnotation(
         CrashReporter::Annotation::JSActorName);
   }
 };
 
+JSActor::JSActor(nsISupports* aGlobal) {
+  mGlobal = do_QueryInterface(aGlobal);
+  if (!mGlobal) {
+    mGlobal = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+  }
+}
+
 void JSActor::StartDestroy() {
   AutoAnnotateMessageInCaseOfCrash guard(/* aActorName = */ mName,
                                          "<WillDestroy>"_ns);
   InvokeCallback(CallbackFunction::WillDestroy);
+  mCanSend = false;
 }
 
 void JSActor::AfterDestroy() {
   AutoAnnotateMessageInCaseOfCrash guard(/* aActorName = */ mName,
                                          "<DidDestroy>"_ns);
+  mCanSend = false;
+
+  // Take our queries out, in case somehow rejecting promises can trigger
+  // additions or removals.
+  nsRefPtrHashtable<nsUint64HashKey, Promise> pendingQueries;
+  mPendingQueries.SwapElements(pendingQueries);
+  for (auto& entry : pendingQueries) {
+    entry.GetData()->MaybeReject(NS_ERROR_NOT_AVAILABLE);
+  }
+
   InvokeCallback(CallbackFunction::DidDestroy);
-  // Clear out & reject mPendingQueries
-  RejectPendingQueries();
+  ClearManager();
 }
 
 void JSActor::InvokeCallback(CallbackFunction callback) {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   AutoEntryScript aes(GetParentObject(), "JSActor destroy callback");
   JSContext* cx = aes.cx();
   MozJSActorCallbacks callbacksHolder;
@@ -126,28 +143,16 @@ nsresult JSActor::QueryInterfaceActor(co
 
     mWrappedJS = do_QueryInterface(wrappedJS);
     MOZ_ASSERT(mWrappedJS);
   }
 
   return mWrappedJS->QueryInterface(aIID, aPtr);
 }
 
-void JSActor::RejectPendingQueries() {
-  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-
-  // Take our queries out, in case somehow rejecting promises can trigger
-  // additions or removals.
-  nsRefPtrHashtable<nsUint64HashKey, Promise> pendingQueries;
-  mPendingQueries.SwapElements(pendingQueries);
-  for (auto iter = pendingQueries.Iter(); !iter.Done(); iter.Next()) {
-    iter.Data()->MaybeReject(NS_ERROR_NOT_AVAILABLE);
-  }
-}
-
 /* static */
 bool JSActor::AllowMessage(const JSActorMessageMeta& aMetadata,
                            size_t aDataLength) {
   // A message includes more than structured clone data, so subtract
   // 20KB to make it more likely that a message within this bound won't
   // result in an overly large IPC message.
   static const size_t kMaxMessageSize =
       IPC::Channel::kMaximumMessageSize - 20 * 1024;
--- a/dom/ipc/jsactor/JSActor.h
+++ b/dom/ipc/jsactor/JSActor.h
@@ -18,55 +18,51 @@
 
 class nsIGlobalObject;
 class nsQueryActorChild;
 class nsQueryActorParent;
 
 namespace mozilla {
 namespace dom {
 
+class JSActorManager;
+class JSActorMessageMeta;
+class QueryPromiseHandler;
+
 enum class JSActorMessageKind {
   Message,
   Query,
   QueryResolve,
   QueryReject,
   EndGuard_,
 };
 
-class JSActorMessageMeta;
-class QueryPromiseHandler;
-
 // Common base class for JSWindowActor{Parent,Child}.
 class JSActor : public nsISupports, public nsWrapperCache {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(JSActor)
 
-  JSActor();
-
-  enum class Type { Parent, Child };
-  enum class CallbackFunction { WillDestroy, DidDestroy, ActorCreated };
+  explicit JSActor(nsISupports* aGlobal = nullptr);
 
   const nsCString& Name() const { return mName; }
 
   void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
                         JS::Handle<JS::Value> aObj, ErrorResult& aRv);
 
   already_AddRefed<Promise> SendQuery(JSContext* aCx,
                                       const nsAString& aMessageName,
                                       JS::Handle<JS::Value> aObj,
                                       ErrorResult& aRv);
 
   void ReceiveRawMessage(const JSActorMessageMeta& aMetadata,
                          ipc::StructuredCloneData&& aData,
                          ipc::StructuredCloneData&& aStack);
 
-  virtual nsIGlobalObject* GetParentObject() const = 0;
-
-  void RejectPendingQueries();
+  nsIGlobalObject* GetParentObject() const { return mGlobal; };
 
  protected:
   // Send the message described by the structured clone data |aData|, and the
   // message metadata |aMetadata|. The underlying transport should call the
   // |ReceiveMessage| method on the other side asynchronously.
   virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
                               ipc::StructuredCloneData&& aData,
                               ipc::StructuredCloneData&& aStack,
@@ -76,23 +72,28 @@ class JSActor : public nsISupports, publ
   // send it. If it is too large, record telemetry about the message.
   static bool AllowMessage(const JSActorMessageMeta& aMetadata,
                            size_t aDataLength);
 
   virtual ~JSActor() = default;
 
   void SetName(const nsACString& aName);
 
+  bool CanSend() const { return mCanSend; }
+
   void StartDestroy();
-
   void AfterDestroy();
 
+  enum class CallbackFunction { WillDestroy, DidDestroy, ActorCreated };
   void InvokeCallback(CallbackFunction willDestroy);
 
+  virtual void ClearManager() = 0;
+
  private:
+  friend class JSActorManager;
   friend class ::nsQueryActorChild;   // for QueryInterfaceActor
   friend class ::nsQueryActorParent;  // for QueryInterfaceActor
 
   nsresult QueryInterfaceActor(const nsIID& aIID, void** aPtr);
 
   void ReceiveMessageOrQuery(JSContext* aCx,
                              const JSActorMessageMeta& aMetadata,
                              JS::Handle<JS::Value> aData, ErrorResult& aRv);
@@ -123,20 +124,22 @@ class JSActor : public nsISupports, publ
                    ipc::StructuredCloneData&& aData);
 
     RefPtr<JSActor> mActor;
     RefPtr<Promise> mPromise;
     nsString mMessageName;
     uint64_t mQueryId;
   };
 
+  nsCOMPtr<nsIGlobalObject> mGlobal;
   nsCOMPtr<nsISupports> mWrappedJS;
   nsCString mName;
   nsRefPtrHashtable<nsUint64HashKey, Promise> mPendingQueries;
-  uint64_t mNextQueryId;
+  uint64_t mNextQueryId = 0;
+  bool mCanSend = true;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 namespace IPC {
 
 template <>
new file mode 100644
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorManager.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/JSActorManager.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozJSComponentLoader.h"
+#include "jsapi.h"
+
+namespace mozilla {
+namespace dom {
+
+already_AddRefed<JSActor> JSActorManager::GetActor(const nsACString& aName,
+                                                   ErrorResult& aRv) {
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+  // If our connection has been closed, return an error.
+  mozilla::ipc::IProtocol* nativeActor = AsNativeActor();
+  if (!nativeActor->CanSend()) {
+    aRv.ThrowInvalidStateError(nsPrintfCString(
+        "Cannot get actor '%s'. Native '%s' actor is destroyed.",
+        PromiseFlatCString(aName).get(), nativeActor->GetProtocolName()));
+    return nullptr;
+  }
+
+  // Check if this actor has already been created, and return it if it has.
+  if (RefPtr<JSActor> actor = mJSActors.Get(aName)) {
+    return actor.forget();
+  }
+
+  RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
+  if (!actorSvc) {
+    aRv.ThrowInvalidStateError("JSActorService hasn't been initialized");
+    return nullptr;
+  }
+
+  // Check if this actor satisfies the requirements of the protocol
+  // corresponding to `aName`, and get the module which implements it.
+  RefPtr<JSActorProtocol> protocol =
+      MatchingJSActorProtocol(actorSvc, aName, aRv);
+  if (!protocol) {
+    return nullptr;
+  }
+
+  bool isParent = nativeActor->GetSide() == mozilla::ipc::ParentSide;
+  auto& side = isParent ? protocol->Parent() : protocol->Child();
+
+  // Constructing an actor requires a running script, so push an AutoEntryScript
+  // onto the stack.
+  AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSActor construction");
+  JSContext* cx = aes.cx();
+
+  // Load the module using mozJSComponentLoader.
+  RefPtr<mozJSComponentLoader> loader = mozJSComponentLoader::Get();
+  MOZ_ASSERT(loader);
+
+  // If a module URI was provided, use it to construct an instance of the actor.
+  JS::RootedObject actorObj(cx);
+  if (side.mModuleURI) {
+    JS::RootedObject global(cx);
+    JS::RootedObject exports(cx);
+    aRv = loader->Import(cx, side.mModuleURI.ref(), &global, &exports);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
+    MOZ_ASSERT(exports, "null exports!");
+
+    // Load the specific property from our module.
+    JS::RootedValue ctor(cx);
+    nsAutoCString ctorName(aName);
+    ctorName.Append(isParent ? "Parent"_ns : "Child"_ns);
+    if (!JS_GetProperty(cx, exports, ctorName.get(), &ctor)) {
+      aRv.NoteJSContextException(cx);
+      return nullptr;
+    }
+
+    if (NS_WARN_IF(!ctor.isObject())) {
+      aRv.ThrowNotFoundError(
+          nsPrintfCString("Could not find actor constructor '%s'",
+                          PromiseFlatCString(ctorName).get()));
+      return nullptr;
+    }
+
+    // Invoke the constructor loaded from the module.
+    if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), &actorObj)) {
+      aRv.NoteJSContextException(cx);
+      return nullptr;
+    }
+  }
+
+  // Initialize our newly-constructed actor, and return it.
+  RefPtr<JSActor> actor = InitJSActor(actorObj, aName, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  mJSActors.Put(aName, RefPtr{actor});
+  return actor.forget();
+}
+
+void JSActorManager::JSActorWillDestroy() {
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+  // Make a copy so that we can avoid potential iterator invalidation when
+  // calling the user-provided Destroy() methods.
+  nsTArray<RefPtr<JSActor>> actors(mJSActors.Count());
+  for (auto& entry : mJSActors) {
+    actors.AppendElement(entry.GetData());
+  }
+  for (auto& actor : actors) {
+    actor->StartDestroy();
+  }
+}
+
+void JSActorManager::JSActorDidDestroy() {
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+  // Swap the table with `mJSActors` so that we don't invalidate it while
+  // iterating.
+  nsRefPtrHashtable<nsCStringHashKey, JSActor> actors;
+  mJSActors.SwapElements(actors);
+  for (auto& entry : actors) {
+    entry.GetData()->AfterDestroy();
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorManager.h
@@ -0,0 +1,72 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_JSActorManager_h
+#define mozilla_dom_JSActorManager_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/dom/JSActor.h"
+#include "mozilla/ErrorResult.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace ipc {
+class IProtocol;
+}
+
+namespace dom {
+
+class JSActorProtocol;
+class JSActorService;
+
+class JSActorManager : public nsISupports {
+ public:
+  /**
+   * Get or create an actor by it's name.
+   *
+   * Will return an error if the actor fails to be constructed, or `nullptr` if
+   * actor creation was vetoed by a constraint.
+   */
+  already_AddRefed<JSActor> GetActor(const nsACString& aName, ErrorResult& aRv);
+
+ protected:
+  /**
+   * Lifecycle methods which will fire the `willDestroy` and `didDestroy`
+   * methods on relevant actors.
+   */
+  void JSActorWillDestroy();
+  void JSActorDidDestroy();
+
+  /**
+   * Return the protocol with the given name, if it is supported by the current
+   * actor.
+   */
+  virtual already_AddRefed<JSActorProtocol> MatchingJSActorProtocol(
+      JSActorService* aActorSvc, const nsACString& aName, ErrorResult& aRv) = 0;
+
+  /**
+   * Initialize a JSActor instance given the constructed JS object.
+   * `aMaybeActor` may be `nullptr`, which should construct the default empty
+   * actor.
+   */
+  virtual already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+                                                const nsACString& aName,
+                                                ErrorResult& aRv) = 0;
+
+  /**
+   * Return this native actor. This should be the same object which is
+   * implementing `JSActorManager`.
+   */
+  virtual mozilla::ipc::IProtocol* AsNativeActor() = 0;
+
+ private:
+  nsRefPtrHashtable<nsCStringHashKey, JSActor> mJSActors;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_JSActorManager_h
\ No newline at end of file
--- a/dom/ipc/jsactor/JSActorService.cpp
+++ b/dom/ipc/jsactor/JSActorService.cpp
@@ -8,18 +8,20 @@
 #include "mozilla/dom/JSActorService.h"
 #include "mozilla/dom/ChromeUtilsBinding.h"
 #include "mozilla/dom/EventListenerBinding.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/JSProcessActorBinding.h"
 #include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/JSProcessActorProtocol.h"
 #include "mozilla/dom/JSWindowActorBinding.h"
 #include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSWindowActorProtocol.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/WindowGlobalChild.h"
 #include "mozilla/ArrayAlgorithm.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Logging.h"
--- a/dom/ipc/jsactor/JSActorService.h
+++ b/dom/ipc/jsactor/JSActorService.h
@@ -9,31 +9,31 @@
 
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIURI.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "mozilla/dom/JSActor.h"
-#include "mozilla/dom/JSProcessActorProtocol.h"
-#include "mozilla/dom/JSWindowActorProtocol.h"
-
 #include "nsIObserver.h"
 #include "nsIDOMEventListener.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/extensions/WebExtensionContentScript.h"
 
 namespace mozilla {
 namespace dom {
+
 struct ProcessActorOptions;
 struct WindowActorOptions;
 class JSProcessActorInfo;
 class JSWindowActorInfo;
 class EventTarget;
+class JSWindowActorProtocol;
+class JSProcessActorProtocol;
 
 class JSActorService final {
  public:
   NS_INLINE_DECL_REFCOUNTING(JSActorService)
 
   static already_AddRefed<JSActorService> GetSingleton();
 
   // Register or unregister a chrome event target.
@@ -81,17 +81,31 @@ class JSActorService final {
   ~JSActorService();
 
   nsTArray<EventTarget*> mChromeEventTargets;
 
   // -- Window Actor
   nsRefPtrHashtable<nsCStringHashKey, JSWindowActorProtocol>
       mWindowActorDescriptors;
 
-  // -- Content Actor
+  // -- Process Actor
   nsRefPtrHashtable<nsCStringHashKey, JSProcessActorProtocol>
       mProcessActorDescriptors;
 };
 
+/**
+ * Base clsas for both `JSWindowActorProtocol` and `JSProcessActorProtocol`
+ * which can be used by generic code.
+ */
+class JSActorProtocol : public nsISupports {
+ public:
+  struct Sided {
+    Maybe<nsCString> mModuleURI;
+  };
+
+  virtual const Sided& Parent() const = 0;
+  virtual const Sided& Child() const = 0;
+};
+
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_JSActorService_h
--- a/dom/ipc/jsactor/JSProcessActorChild.cpp
+++ b/dom/ipc/jsactor/JSProcessActorChild.cpp
@@ -60,17 +60,17 @@ class AsyncMessageToProcessParent : publ
 };
 
 }  // namespace
 
 void JSProcessActorChild::SendRawMessage(const JSActorMessageMeta& aMeta,
                                          ipc::StructuredCloneData&& aData,
                                          ipc::StructuredCloneData&& aStack,
                                          ErrorResult& aRv) {
-  if (NS_WARN_IF(!mCanSend || !mManager || !mManager->GetCanSend())) {
+  if (NS_WARN_IF(!CanSend() || !mManager || !mManager->GetCanSend())) {
     aRv.ThrowInvalidStateError("JSProcessActorChild cannot send at the moment");
     return;
   }
 
   if (NS_WARN_IF(
           !AllowMessage(aMeta, aData.DataLength() + aStack.DataLength()))) {
     aRv.ThrowDataCloneError(
         nsPrintfCString("JSProcessActorChild serialization error: data too "
@@ -113,15 +113,12 @@ void JSProcessActorChild::Init(const nsA
                                nsIDOMProcessChild* aManager) {
   MOZ_ASSERT(!mManager, "Cannot Init() a JSProcessActorChild twice!");
   SetName(aName);
   mManager = aManager;
 
   InvokeCallback(CallbackFunction::ActorCreated);
 }
 
-void JSProcessActorChild::AfterDestroy() {
-  JSActor::AfterDestroy();
-  mManager = nullptr;
-}
+void JSProcessActorChild::ClearManager() { mManager = nullptr; }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/jsactor/JSProcessActorChild.h
+++ b/dom/ipc/jsactor/JSProcessActorChild.h
@@ -15,45 +15,43 @@ namespace dom {
 
 // Placeholder implementation.
 class JSProcessActorChild final : public JSActor {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSProcessActorChild,
                                                          JSActor)
 
-  nsIGlobalObject* GetParentObject() const override {
-    return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-  }
+  explicit JSProcessActorChild(nsISupports* aGlobal = nullptr)
+      : JSActor(aGlobal) {}
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<JSProcessActorChild> Constructor(
       GlobalObject& aGlobal) {
-    return MakeAndAddRef<JSProcessActorChild>();
+    return MakeAndAddRef<JSProcessActorChild>(aGlobal.GetAsSupports());
   }
 
   nsIDOMProcessChild* Manager() const { return mManager; }
 
   void Init(const nsACString& aName, nsIDOMProcessChild* aManager);
-  void AfterDestroy();
+  void ClearManager() override;
 
  protected:
   // Send the message described by the structured clone data |aData|, and the
   // message metadata |aMetadata|. The underlying transport should call the
   // |ReceiveMessage| method on the other side asynchronously.
   virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
                               ipc::StructuredCloneData&& aData,
                               ipc::StructuredCloneData&& aStack,
                               ErrorResult& aRv) override;
 
  private:
   ~JSProcessActorChild() { MOZ_ASSERT(!mManager); }
 
-  bool mCanSend = true;
   nsCOMPtr<nsIDOMProcessChild> mManager;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_JSProcessActorChild_h
--- a/dom/ipc/jsactor/JSProcessActorParent.cpp
+++ b/dom/ipc/jsactor/JSProcessActorParent.cpp
@@ -71,17 +71,17 @@ class AsyncMessageToProcessChild : publi
 };
 
 }  // namespace
 
 void JSProcessActorParent::SendRawMessage(const JSActorMessageMeta& aMeta,
                                           ipc::StructuredCloneData&& aData,
                                           ipc::StructuredCloneData&& aStack,
                                           ErrorResult& aRv) {
-  if (NS_WARN_IF(!mCanSend || !mManager || !mManager->GetCanSend())) {
+  if (NS_WARN_IF(!CanSend() || !mManager || !mManager->GetCanSend())) {
     aRv.ThrowInvalidStateError(
         nsPrintfCString("Actor '%s' cannot send message '%s' during shutdown.",
                         PromiseFlatCString(aMeta.actorName()).get(),
                         NS_ConvertUTF16toUTF8(aMeta.messageName()).get()));
     return;
   }
 
   if (NS_WARN_IF(
@@ -117,15 +117,12 @@ void JSProcessActorParent::SendRawMessag
   }
 
   if (NS_WARN_IF(!contentParent->SendRawMessage(aMeta, msgData, stackData))) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 }
 
-void JSProcessActorParent::AfterDestroy() {
-  JSActor::AfterDestroy();
-  mManager = nullptr;
-}
+void JSProcessActorParent::ClearManager() { mManager = nullptr; }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/jsactor/JSProcessActorParent.h
+++ b/dom/ipc/jsactor/JSProcessActorParent.h
@@ -22,45 +22,43 @@ namespace mozilla {
 namespace dom {
 
 class JSProcessActorParent final : public JSActor {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSProcessActorParent,
                                                          JSActor)
 
-  nsIGlobalObject* GetParentObject() const override {
-    return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-  }
+  explicit JSProcessActorParent(nsISupports* aGlobal = nullptr)
+      : JSActor(aGlobal) {}
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<JSProcessActorParent> Constructor(
       GlobalObject& aGlobal) {
-    return MakeAndAddRef<JSProcessActorParent>();
+    return MakeAndAddRef<JSProcessActorParent>(aGlobal.GetAsSupports());
   }
 
   nsIDOMProcessParent* Manager() const { return mManager; }
 
   void Init(const nsACString& aName, nsIDOMProcessParent* aManager);
-  void AfterDestroy();
+  void ClearManager() override;
 
  protected:
   // Send the message described by the structured clone data |aData|, and the
   // message metadata |aMetadata|. The underlying transport should call the
   // |ReceiveMessage| method on the other side asynchronously.
   virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
                               ipc::StructuredCloneData&& aData,
                               ipc::StructuredCloneData&& aStack,
                               ErrorResult& aRv) override;
 
  private:
   ~JSProcessActorParent();
 
-  bool mCanSend = true;
   nsCOMPtr<nsIDOMProcessParent> mManager;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_JSProcessActorParent_h
--- a/dom/ipc/jsactor/JSProcessActorProtocol.cpp
+++ b/dom/ipc/jsactor/JSProcessActorProtocol.cpp
@@ -74,31 +74,27 @@ JSProcessActorProtocol::FromWebIDLOption
   return proto.forget();
 }
 
 NS_IMETHODIMP JSProcessActorProtocol::Observe(nsISupports* aSubject,
                                               const char* aTopic,
                                               const char16_t* aData) {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
-  nsCOMPtr<nsIDOMProcessChild> manager;
+  RefPtr<JSActorManager> manager;
   if (XRE_IsParentProcess()) {
     manager = InProcessChild::Singleton();
   } else {
     manager = ContentChild::GetSingleton();
   }
 
-  RefPtr<JSProcessActorChild> actor;
-  nsresult rv = manager->GetActor(mName, getter_AddRefs(actor));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    // Don't raise an error if creation of our actor was vetoed.
-    if (rv == NS_ERROR_NOT_AVAILABLE) {
-      return NS_OK;
-    }
-    return rv;
+  // Ensure our actor is present.
+  RefPtr<JSActor> actor = manager->GetActor(mName, IgnoreErrors());
+  if (!actor) {
+    return NS_OK;
   }
 
   // Build a observer callback.
   JS::Rooted<JSObject*> global(RootingCx(),
                                JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
   RefPtr<MozObserverCallback> observerCallback =
       new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
   observerCallback->Observe(aSubject, nsDependentCString(aTopic),
--- a/dom/ipc/jsactor/JSProcessActorProtocol.h
+++ b/dom/ipc/jsactor/JSProcessActorProtocol.h
@@ -3,16 +3,17 @@
 /* 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_JSProcessActorProtocol_h
 #define mozilla_dom_JSProcessActorProtocol_h
 
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/JSActorService.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIURI.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIObserver.h"
 
 namespace mozilla {
 namespace dom {
@@ -22,42 +23,39 @@ class JSProcessActorInfo;
 class EventTarget;
 
 /**
  * Object corresponding to a single process actor protocol
  *
  * This object also can act as a carrier for methods and other state related to
  * a single protocol managed by the JSActorService.
  */
-class JSProcessActorProtocol final : public nsIObserver {
+class JSProcessActorProtocol final : public JSActorProtocol,
+                                     public nsIObserver {
  public:
   NS_DECL_NSIOBSERVER
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(JSProcessActorProtocol, nsIObserver)
 
   static already_AddRefed<JSProcessActorProtocol> FromIPC(
       const JSProcessActorInfo& aInfo);
   JSProcessActorInfo ToIPC();
 
   static already_AddRefed<JSProcessActorProtocol> FromWebIDLOptions(
       const nsACString& aName, const ProcessActorOptions& aOptions,
       ErrorResult& aRv);
 
-  struct Sided {
-    Maybe<nsCString> mModuleURI;
-  };
-
   struct ParentSide : public Sided {};
 
   struct ChildSide : public Sided {
     nsTArray<nsCString> mObservers;
   };
 
-  const ParentSide& Parent() const { return mParent; }
-  const ChildSide& Child() const { return mChild; }
+  const ParentSide& Parent() const override { return mParent; }
+  const ChildSide& Child() const override { return mChild; }
 
   void AddObservers();
   void RemoveObservers();
   bool Matches(const nsAString& aRemoteType);
 
  private:
   explicit JSProcessActorProtocol(const nsACString& aName) : mName(aName) {}
   bool RemoteTypePrefixMatches(const nsDependentSubstring& aRemoteType);
--- a/dom/ipc/jsactor/JSWindowActorChild.cpp
+++ b/dom/ipc/jsactor/JSWindowActorChild.cpp
@@ -12,20 +12,16 @@
 #include "mozilla/dom/WindowProxyHolder.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "nsGlobalWindowInner.h"
 
 namespace mozilla {
 namespace dom {
 
-JSWindowActorChild::JSWindowActorChild(nsIGlobalObject* aGlobal)
-    : mGlobal(aGlobal ? aGlobal
-                      : xpc::NativeGlobal(xpc::PrivilegedJunkScope())) {}
-
 JSWindowActorChild::~JSWindowActorChild() { MOZ_ASSERT(!mManager); }
 
 JSObject* JSWindowActorChild::WrapObject(JSContext* aCx,
                                          JS::Handle<JSObject*> aGivenProto) {
   return JSWindowActorChild_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 WindowGlobalChild* JSWindowActorChild::GetManager() const { return mManager; }
@@ -70,17 +66,17 @@ class AsyncMessageToParent : public Runn
 };
 
 }  // anonymous namespace
 
 void JSWindowActorChild::SendRawMessage(const JSActorMessageMeta& aMeta,
                                         ipc::StructuredCloneData&& aData,
                                         ipc::StructuredCloneData&& aStack,
                                         ErrorResult& aRv) {
-  if (NS_WARN_IF(!mCanSend || !mManager || !mManager->CanSend())) {
+  if (NS_WARN_IF(!CanSend() || !mManager || !mManager->CanSend())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (mManager->IsInProcess()) {
     nsCOMPtr<nsIRunnable> runnable = new AsyncMessageToParent(
         aMeta, std::move(aData), std::move(aStack), mManager);
     NS_DispatchToMainThread(runnable.forget());
@@ -139,25 +135,17 @@ nsIDocShell* JSWindowActorChild::GetDocS
 Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow(
     ErrorResult& aRv) {
   if (BrowsingContext* bc = GetBrowsingContext(aRv)) {
     return WindowProxyHolder(bc);
   }
   return nullptr;
 }
 
-void JSWindowActorChild::StartDestroy() {
-  JSActor::StartDestroy();
-  mCanSend = false;
-}
-
-void JSWindowActorChild::AfterDestroy() {
-  JSActor::AfterDestroy();
-  mManager = nullptr;
-}
+void JSWindowActorChild::ClearManager() { mManager = nullptr; }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(JSWindowActorChild, JSActor, mManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSWindowActorChild, JSActor)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorChild)
 NS_INTERFACE_MAP_END_INHERITING(JSActor)
--- a/dom/ipc/jsactor/JSWindowActorChild.h
+++ b/dom/ipc/jsactor/JSWindowActorChild.h
@@ -34,49 +34,44 @@ namespace mozilla {
 namespace dom {
 
 class JSWindowActorChild final : public JSActor {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSWindowActorChild,
                                                          JSActor)
 
-  explicit JSWindowActorChild(nsIGlobalObject* aGlobal = nullptr);
-
-  nsIGlobalObject* GetParentObject() const override { return mGlobal; }
+  explicit JSWindowActorChild(nsISupports* aGlobal = nullptr)
+      : JSActor(aGlobal) {}
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<JSWindowActorChild> Constructor(
       GlobalObject& aGlobal) {
-    nsCOMPtr<nsIGlobalObject> global(
-        do_QueryInterface(aGlobal.GetAsSupports()));
-    return MakeAndAddRef<JSWindowActorChild>(global);
+    return MakeAndAddRef<JSWindowActorChild>(aGlobal.GetAsSupports());
   }
 
   WindowGlobalChild* GetManager() const;
   void Init(const nsACString& aName, WindowGlobalChild* aManager);
-  void StartDestroy();
-  void AfterDestroy();
+  void ClearManager() override;
   Document* GetDocument(ErrorResult& aRv);
   BrowsingContext* GetBrowsingContext(ErrorResult& aRv);
   nsIDocShell* GetDocShell(ErrorResult& aRv);
   Nullable<WindowProxyHolder> GetContentWindow(ErrorResult& aRv);
 
  protected:
   void SendRawMessage(const JSActorMessageMeta& aMeta,
                       ipc::StructuredCloneData&& aData,
                       ipc::StructuredCloneData&& aStack,
                       ErrorResult& aRv) override;
 
  private:
   ~JSWindowActorChild();
 
-  bool mCanSend = true;
   RefPtr<WindowGlobalChild> mManager;
 
   nsCOMPtr<nsIGlobalObject> mGlobal;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/ipc/jsactor/JSWindowActorParent.cpp
+++ b/dom/ipc/jsactor/JSWindowActorParent.cpp
@@ -11,20 +11,16 @@
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 JSWindowActorParent::~JSWindowActorParent() { MOZ_ASSERT(!mManager); }
 
-nsIGlobalObject* JSWindowActorParent::GetParentObject() const {
-  return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
-}
-
 JSObject* JSWindowActorParent::WrapObject(JSContext* aCx,
                                           JS::Handle<JSObject*> aGivenProto) {
   return JSWindowActorParent_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 WindowGlobalParent* JSWindowActorParent::GetManager() const { return mManager; }
 
 void JSWindowActorParent::Init(const nsACString& aName,
@@ -67,17 +63,17 @@ class AsyncMessageToChild : public Runna
 };
 
 }  // anonymous namespace
 
 void JSWindowActorParent::SendRawMessage(const JSActorMessageMeta& aMeta,
                                          ipc::StructuredCloneData&& aData,
                                          ipc::StructuredCloneData&& aStack,
                                          ErrorResult& aRv) {
-  if (NS_WARN_IF(!mCanSend || !mManager || !mManager->CanSend())) {
+  if (NS_WARN_IF(!CanSend() || !mManager || !mManager->CanSend())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (mManager->IsInProcess()) {
     nsCOMPtr<nsIRunnable> runnable = new AsyncMessageToChild(
         aMeta, std::move(aData), std::move(aStack), mManager);
     NS_DispatchToMainThread(runnable.forget());
@@ -112,25 +108,17 @@ CanonicalBrowsingContext* JSWindowActorP
   if (!mManager) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   return mManager->BrowsingContext();
 }
 
-void JSWindowActorParent::StartDestroy() {
-  JSActor::StartDestroy();
-  mCanSend = false;
-}
-
-void JSWindowActorParent::AfterDestroy() {
-  JSActor::AfterDestroy();
-  mManager = nullptr;
-}
+void JSWindowActorParent::ClearManager() { mManager = nullptr; }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(JSWindowActorParent, JSActor, mManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSWindowActorParent, JSActor)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorParent)
 NS_INTERFACE_MAP_END_INHERITING(JSActor)
--- a/dom/ipc/jsactor/JSWindowActorParent.h
+++ b/dom/ipc/jsactor/JSWindowActorParent.h
@@ -28,41 +28,40 @@ namespace mozilla {
 namespace dom {
 
 class JSWindowActorParent final : public JSActor {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSWindowActorParent,
                                                          JSActor)
 
+  explicit JSWindowActorParent(nsISupports* aGlobal = nullptr)
+      : JSActor(aGlobal) {}
+
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<JSWindowActorParent> Constructor(
       GlobalObject& aGlobal) {
-    return MakeAndAddRef<JSWindowActorParent>();
+    return MakeAndAddRef<JSWindowActorParent>(aGlobal.GetAsSupports());
   }
 
-  nsIGlobalObject* GetParentObject() const override;
-
   WindowGlobalParent* GetManager() const;
   void Init(const nsACString& aName, WindowGlobalParent* aManager);
-  void StartDestroy();
-  void AfterDestroy();
+  void ClearManager() override;
   CanonicalBrowsingContext* GetBrowsingContext(ErrorResult& aRv);
 
  protected:
   void SendRawMessage(const JSActorMessageMeta& aMeta,
                       ipc::StructuredCloneData&& aData,
                       ipc::StructuredCloneData&& aStack,
                       ErrorResult& aRv) override;
 
  private:
   ~JSWindowActorParent();
 
-  bool mCanSend = true;
   RefPtr<WindowGlobalParent> mManager;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_JSWindowActorParent_h
--- a/dom/ipc/jsactor/JSWindowActorProtocol.cpp
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.cpp
@@ -174,26 +174,19 @@ NS_IMETHODIMP JSWindowActorProtocol::Han
   }
 
   RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild();
   if (NS_WARN_IF(!wgc)) {
     return NS_ERROR_FAILURE;
   }
 
   // Ensure our actor is present.
-  ErrorResult error;
-  RefPtr<JSWindowActorChild> actor = wgc->GetActor(mName, error);
-  if (error.Failed()) {
-    nsresult rv = error.StealNSResult();
-
-    // Don't raise an error if creation of our actor was vetoed.
-    if (rv == NS_ERROR_NOT_AVAILABLE) {
-      return NS_OK;
-    }
-    return rv;
+  RefPtr<JSActor> actor = wgc->GetActor(mName, IgnoreErrors());
+  if (!actor) {
+    return NS_OK;
   }
 
   // Build our event listener & call it.
   JS::Rooted<JSObject*> global(RootingCx(),
                                JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
   RefPtr<EventListener> eventListener =
       new EventListener(actor->GetWrapper(), global, nullptr, nullptr);
   eventListener->HandleEvent(*aEvent, "JSWindowActorProtocol::HandleEvent");
@@ -218,26 +211,19 @@ NS_IMETHODIMP JSWindowActorProtocol::Obs
     wgc = inner->GetWindowGlobalChild();
   }
 
   if (NS_WARN_IF(!wgc)) {
     return NS_ERROR_FAILURE;
   }
 
   // Ensure our actor is present.
-  ErrorResult error;
-  RefPtr<JSWindowActorChild> actor = wgc->GetActor(mName, error);
-  if (NS_WARN_IF(error.Failed())) {
-    nsresult rv = error.StealNSResult();
-
-    // Don't raise an error if creation of our actor was vetoed.
-    if (rv == NS_ERROR_NOT_AVAILABLE) {
-      return NS_OK;
-    }
-    return rv;
+  RefPtr<JSActor> actor = wgc->GetActor(mName, IgnoreErrors());
+  if (!actor) {
+    return NS_OK;
   }
 
   // Build a observer callback.
   JS::Rooted<JSObject*> global(RootingCx(),
                                JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
   RefPtr<MozObserverCallback> observerCallback =
       new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
   observerCallback->Observe(aSubject, nsDependentCString(aTopic),
--- a/dom/ipc/jsactor/JSWindowActorProtocol.h
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.h
@@ -3,16 +3,17 @@
 /* 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_JSWindowActorProtocol_h
 #define mozilla_dom_JSWindowActorProtocol_h
 
 #include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/JSActorService.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIURI.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIObserver.h"
 #include "nsIDOMEventListener.h"
 #include "mozilla/extensions/WebExtensionContentScript.h"
 
@@ -26,51 +27,48 @@ class EventTarget;
 /**
  * Object corresponding to a single window actor protocol. This object acts as
  * an Event listener for the actor which is called for events which would
  * trigger actor creation.
  *
  * This object also can act as a carrier for methods and other state related to
  * a single protocol managed by the JSActorService.
  */
-class JSWindowActorProtocol final : public nsIObserver,
+class JSWindowActorProtocol final : public JSActorProtocol,
+                                    public nsIObserver,
                                     public nsIDOMEventListener {
  public:
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(JSWindowActorProtocol, nsIObserver)
 
   static already_AddRefed<JSWindowActorProtocol> FromIPC(
       const JSWindowActorInfo& aInfo);
   JSWindowActorInfo ToIPC();
 
   static already_AddRefed<JSWindowActorProtocol> FromWebIDLOptions(
       const nsACString& aName, const WindowActorOptions& aOptions,
       ErrorResult& aRv);
 
-  struct Sided {
-    Maybe<nsCString> mModuleURI;
-  };
-
   struct ParentSide : public Sided {};
 
   struct EventDecl {
     nsString mName;
     EventListenerFlags mFlags;
     Optional<bool> mPassive;
   };
 
   struct ChildSide : public Sided {
     nsTArray<EventDecl> mEvents;
     nsTArray<nsCString> mObservers;
   };
 
-  const ParentSide& Parent() const { return mParent; }
-  const ChildSide& Child() const { return mChild; }
+  const ParentSide& Parent() const override { return mParent; }
+  const ChildSide& Child() const override { return mChild; }
 
   void RegisterListenersFor(EventTarget* aTarget);
   void UnregisterListenersFor(EventTarget* aTarget);
   void AddObservers();
   void RemoveObservers();
   bool Matches(BrowsingContext* aBrowsingContext, nsIURI* aURI,
                const nsAString& aRemoteType);
 
--- a/dom/ipc/jsactor/moz.build
+++ b/dom/ipc/jsactor/moz.build
@@ -1,35 +1,38 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.dom += [
     'JSActor.h',
+    'JSActorManager.h',
     'JSActorService.h',
     'JSProcessActorChild.h',
     'JSProcessActorParent.h',
     'JSProcessActorProtocol.h',
     'JSWindowActorChild.h',
     'JSWindowActorParent.h',
     'JSWindowActorProtocol.h',
 ]
 
 UNIFIED_SOURCES += [
     'JSActor.cpp',
+    'JSActorManager.cpp',
     'JSActorService.cpp',
     'JSProcessActorChild.cpp',
     'JSProcessActorParent.cpp',
     'JSProcessActorProtocol.cpp',
     'JSWindowActorChild.cpp',
     'JSWindowActorParent.cpp',
     'JSWindowActorProtocol.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '/js/xpconnect/loader',
     '/js/xpconnect/src',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'