Bug 1649477 - Part 3: Handle ReceiveRawMessage generically, r=kmag
authorNika Layzell <nika@thelayzells.com>
Wed, 08 Jul 2020 14:22:22 +0000
changeset 539397 d7810f45f0cbbf63ec61112e4d5c16433640775b
parent 539396 d3c935827ef7694b50f5b2d9fc2665d27f9c27c2
child 539398 5907260fa4688e06eb4b07982ed23fc2d476d8e3
push id121100
push usernlayzell@mozilla.com
push dateWed, 08 Jul 2020 16:43:08 +0000
treeherderautoland@5907260fa468 [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 3: Handle ReceiveRawMessage generically, r=kmag Differential Revision: https://phabricator.services.mozilla.com/D82102
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
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/JSProcessActorChild.cpp
dom/ipc/jsactor/JSProcessActorParent.cpp
dom/ipc/jsactor/JSWindowActorChild.cpp
dom/ipc/jsactor/JSWindowActorParent.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -4291,25 +4291,21 @@ already_AddRefed<JSActor> ContentChild::
                      "mManager was already initialized once!");
   actor->Init(aName, this);
   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) {
-    StructuredCloneData data;
-    data.BorrowFromClonedMessageDataForChild(aData);
-    StructuredCloneData stack;
-    stack.BorrowFromClonedMessageDataForChild(aStack);
-    actor->ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
-  }
+  StructuredCloneData data;
+  data.BorrowFromClonedMessageDataForChild(aData);
+  StructuredCloneData stack;
+  stack.BorrowFromClonedMessageDataForChild(aStack);
+  ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
   return IPC_OK();
 }
 
 NS_IMETHODIMP ContentChild::GetCanSend(bool* aCanSend) {
   *aCanSend = CanSend();
   return NS_OK;
 }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -6850,25 +6850,21 @@ NS_IMETHODIMP ContentParent::GetChildID(
 NS_IMETHODIMP ContentParent::GetOsPid(int32_t* aOut) {
   *aOut = Pid();
   return NS_OK;
 }
 
 IPCResult ContentParent::RecvRawMessage(const JSActorMessageMeta& aMeta,
                                         const ClonedMessageData& aData,
                                         const ClonedMessageData& aStack) {
-  RefPtr<JSProcessActorParent> actor;
-  GetActor(aMeta.actorName(), getter_AddRefs(actor));
-  if (actor) {
-    StructuredCloneData data;
-    data.BorrowFromClonedMessageDataForParent(aData);
-    StructuredCloneData stack;
-    stack.BorrowFromClonedMessageDataForParent(aStack);
-    actor->ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
-  }
+  StructuredCloneData data;
+  data.BorrowFromClonedMessageDataForParent(aData);
+  StructuredCloneData stack;
+  stack.BorrowFromClonedMessageDataForParent(aStack);
+  ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
   return IPC_OK();
 }
 
 NS_IMETHODIMP ContentParent::GetActor(const nsACString& aName,
                                       JSProcessActorParent** retval) {
   ErrorResult error;
   RefPtr<JSProcessActorParent> actor =
       JSActorManager::GetActor(aName, error).downcast<JSProcessActorParent>();
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -543,26 +543,16 @@ IPCResult WindowGlobalChild::RecvRawMess
   StructuredCloneData data;
   data.BorrowFromClonedMessageDataForChild(aData);
   StructuredCloneData stack;
   stack.BorrowFromClonedMessageDataForChild(aStack);
   ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
   return IPC_OK();
 }
 
-void WindowGlobalChild::ReceiveRawMessage(const JSActorMessageMeta& aMeta,
-                                          StructuredCloneData&& aData,
-                                          StructuredCloneData&& aStack) {
-  RefPtr<JSWindowActorChild> actor =
-      GetActor(aMeta.actorName(), IgnoreErrors());
-  if (actor) {
-    actor->ReceiveRawMessage(aMeta, std::move(aData), std::move(aStack));
-  }
-}
-
 void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) {
 #ifdef MOZ_GECKO_PROFILER
   // Registers a DOM Window with the profiler. It re-registers the same Inner
   // Window ID with different URIs because when a Browsing context is first
   // loaded, the first url loaded in it will be about:blank. This call keeps the
   // first non-about:blank registration of window and discards the previous one.
   uint64_t embedderInnerWindowID = 0;
   if (BrowsingContext()->GetParent()) {
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -84,20 +84,16 @@ class WindowGlobalChild final : public W
   // Get the other side of this actor if it is an in-process actor. Returns
   // |nullptr| if the actor has been torn down, or is not in-process.
   already_AddRefed<WindowGlobalParent> GetParentActor();
 
   // Get this actor's manager if it is not an in-process actor. Returns
   // |nullptr| if the actor has been torn down, or is in-process.
   already_AddRefed<BrowserChild> GetBrowserChild();
 
-  void ReceiveRawMessage(const JSActorMessageMeta& aMeta,
-                         ipc::StructuredCloneData&& aData,
-                         ipc::StructuredCloneData&& aStack);
-
   // Get a JS actor object by name.
   already_AddRefed<JSWindowActorChild> GetActor(const nsACString& aName,
                                                 ErrorResult& aRv);
 
   // Create and initialize the WindowGlobalChild object.
   static already_AddRefed<WindowGlobalChild> Create(
       nsGlobalWindowInner* aWindow);
   static already_AddRefed<WindowGlobalChild> CreateDisconnected(
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -420,26 +420,16 @@ IPCResult WindowGlobalParent::RecvRawMes
   StructuredCloneData data;
   data.BorrowFromClonedMessageDataForParent(aData);
   StructuredCloneData stack;
   stack.BorrowFromClonedMessageDataForParent(aStack);
   ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
   return IPC_OK();
 }
 
-void WindowGlobalParent::ReceiveRawMessage(const JSActorMessageMeta& aMeta,
-                                           StructuredCloneData&& aData,
-                                           StructuredCloneData&& aStack) {
-  RefPtr<JSWindowActorParent> actor =
-      GetActor(aMeta.actorName(), IgnoreErrors());
-  if (actor) {
-    actor->ReceiveRawMessage(aMeta, std::move(aData), std::move(aStack));
-  }
-}
-
 const nsAString& WindowGlobalParent::GetRemoteType() {
   if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
     return browserParent->Manager()->GetRemoteType();
   }
 
   return VoidString();
 }
 
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -90,20 +90,16 @@ class WindowGlobalParent final : public 
                                                  ErrorResult& aRv);
 
   // Get this actor's manager if it is not an in-process actor. Returns
   // |nullptr| if the actor has been torn down, or is in-process.
   already_AddRefed<BrowserParent> GetBrowserParent();
 
   ContentParent* GetContentParent();
 
-  void ReceiveRawMessage(const JSActorMessageMeta& aMeta,
-                         ipc::StructuredCloneData&& aData,
-                         ipc::StructuredCloneData&& aStack);
-
   // The principal of this WindowGlobal. This value will not change over the
   // lifetime of the WindowGlobal object, even to reflect changes in
   // |document.domain|.
   nsIPrincipal* DocumentPrincipal() { return mDocumentPrincipal; }
 
   nsIPrincipal* ContentBlockingAllowListPrincipal() {
     return mDocContentBlockingAllowListPrincipal;
   }
--- a/dom/ipc/jsactor/JSActor.cpp
+++ b/dom/ipc/jsactor/JSActor.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/dom/JSActorBinding.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/ClonedErrorHolder.h"
 #include "mozilla/dom/ClonedErrorHolderBinding.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/dom/JSActorManager.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/PWindowGlobal.h"
 #include "mozilla/dom/Promise.h"
 #include "js/Promise.h"
 #include "xpcprivate.h"
 #include "nsASCIIMask.h"
 #include "nsICrashReporter.h"
 
@@ -44,51 +45,29 @@ 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)
 
-// 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(
-        CrashReporter::Annotation::JSActorMessage, aMessageName);
-  }
-  ~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);
@@ -254,79 +233,16 @@ already_AddRefed<Promise> JSActor::SendQ
   meta.kind() = JSActorMessageKind::Query;
 
   mPendingQueries.Put(meta.queryId(), RefPtr{promise});
 
   SendRawMessage(meta, std::move(data), CaptureJSStack(aCx), aRv);
   return promise.forget();
 }
 
-#define CHILD_DIAGNOSTIC_ASSERT(test, msg) \
-  do {                                     \
-    if (XRE_IsParentProcess()) {           \
-      MOZ_ASSERT(test, msg);               \
-    } else {                               \
-      MOZ_DIAGNOSTIC_ASSERT(test, msg);    \
-    }                                      \
-  } while (0)
-
-void JSActor::ReceiveRawMessage(const JSActorMessageMeta& aMetadata,
-                                ipc::StructuredCloneData&& aData,
-                                ipc::StructuredCloneData&& aStack) {
-  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-  AutoAnnotateMessageInCaseOfCrash guard(
-      /* aActorName = */ mName,
-      NS_LossyConvertUTF16toASCII(aMetadata.messageName()));
-
-  AutoEntryScript aes(GetParentObject(), "JSActor message handler");
-  JSContext* cx = aes.cx();
-
-  // Read the message into a JS object from IPC.
-  ErrorResult error;
-  JS::Rooted<JS::Value> data(cx);
-  aData.Read(cx, &data, error);
-  if (NS_WARN_IF(error.Failed())) {
-    CHILD_DIAGNOSTIC_ASSERT(false, "Should not receive non-decodable data");
-    MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(cx));
-    return;
-  }
-
-  JS::Rooted<JSObject*> stack(cx);
-  Maybe<JS::AutoSetAsyncStackForNewCalls> stackSetter;
-  {
-    JS::Rooted<JS::Value> stackVal(cx);
-    aStack.Read(cx, &stackVal, IgnoreErrors());
-    if (stackVal.isObject()) {
-      stack = &stackVal.toObject();
-      if (!js::IsSavedFrame(stack)) {
-        CHILD_DIAGNOSTIC_ASSERT(false, "Stack must be a SavedFrame object");
-        return;
-      }
-      stackSetter.emplace(cx, stack, "JSActor query");
-    }
-  }
-
-  switch (aMetadata.kind()) {
-    case JSActorMessageKind::QueryResolve:
-    case JSActorMessageKind::QueryReject:
-      ReceiveQueryReply(cx, aMetadata, data, error);
-      break;
-
-    case JSActorMessageKind::Message:
-    case JSActorMessageKind::Query:
-      ReceiveMessageOrQuery(cx, aMetadata, data, error);
-      break;
-
-    default:
-      MOZ_ASSERT_UNREACHABLE();
-  }
-
-  Unused << error.MaybeSetPendingException(cx);
-}
-
 void JSActor::ReceiveMessageOrQuery(JSContext* aCx,
                                     const JSActorMessageMeta& aMetadata,
                                     JS::Handle<JS::Value> aData,
                                     ErrorResult& aRv) {
   // The argument which we want to pass to IPC.
   RootedDictionary<ReceiveMessageArgument> argument(aCx);
   argument.mTarget = this;
   argument.mName = aMetadata.messageName();
@@ -399,16 +315,32 @@ void JSActor::ReceiveQueryReply(JSContex
 
   if (aMetadata.kind() == JSActorMessageKind::QueryResolve) {
     promise->MaybeResolve(data);
   } else {
     promise->MaybeReject(data);
   }
 }
 
+void JSActor::SendRawMessageInProcess(const JSActorMessageMeta& aMeta,
+                                      ipc::StructuredCloneData&& aData,
+                                      ipc::StructuredCloneData&& aStack,
+                                      OtherSideCallback&& aGetOtherSide) {
+  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+      "JSActor Async Message",
+      [aMeta, data{std::move(aData)}, stack{std::move(aStack)},
+       getOtherSide{std::move(aGetOtherSide)}]() mutable {
+        if (RefPtr<JSActorManager> otherSide = getOtherSide()) {
+          otherSide->ReceiveRawMessage(aMeta, std::move(data),
+                                       std::move(stack));
+        }
+      }));
+}
+
 // Native handler for our generated promise which is used to handle Queries and
 // send the reply when their promises have been resolved.
 JSActor::QueryHandler::QueryHandler(JSActor* aActor,
                                     const JSActorMessageMeta& aMetadata,
                                     Promise* aPromise)
     : mActor(aActor),
       mPromise(aPromise),
       mMessageName(aMetadata.messageName()),
--- a/dom/ipc/jsactor/JSActor.h
+++ b/dom/ipc/jsactor/JSActor.h
@@ -48,36 +48,39 @@ class JSActor : public nsISupports, publ
   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);
-
   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,
                               ErrorResult& aRv) = 0;
 
   // Check if a message is so large that IPC will probably crash if we try to
   // send it. If it is too large, record telemetry about the message.
   static bool AllowMessage(const JSActorMessageMeta& aMetadata,
                            size_t aDataLength);
 
+  // Helper method to send an in-process raw message.
+  using OtherSideCallback = std::function<already_AddRefed<JSActorManager>()>;
+  static void SendRawMessageInProcess(const JSActorMessageMeta& aMeta,
+                                      ipc::StructuredCloneData&& aData,
+                                      ipc::StructuredCloneData&& aStack,
+                                      OtherSideCallback&& aGetOtherSide);
+
   virtual ~JSActor() = default;
 
   void SetName(const nsACString& aName);
 
   bool CanSend() const { return mCanSend; }
 
   void StartDestroy();
   void AfterDestroy();
@@ -89,20 +92,21 @@ class JSActor : public nsISupports, publ
 
  private:
   friend class JSActorManager;
   friend class ::nsQueryActorChild;   // for QueryInterfaceActor
   friend class ::nsQueryActorParent;  // for QueryInterfaceActor
 
   nsresult QueryInterfaceActor(const nsIID& aIID, void** aPtr);
 
+  // Called by JSActorManager when they receive raw message data destined for
+  // this actor.
   void ReceiveMessageOrQuery(JSContext* aCx,
                              const JSActorMessageMeta& aMetadata,
                              JS::Handle<JS::Value> aData, ErrorResult& aRv);
-
   void ReceiveQueryReply(JSContext* aCx, const JSActorMessageMeta& aMetadata,
                          JS::Handle<JS::Value> aData, ErrorResult& aRv);
 
   // Helper object used while processing query messages to send the final reply
   // message.
   class QueryHandler final : public PromiseNativeHandler {
    public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
--- a/dom/ipc/jsactor/JSActorManager.cpp
+++ b/dom/ipc/jsactor/JSActorManager.cpp
@@ -72,19 +72,18 @@ already_AddRefed<JSActor> JSActorManager
     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()));
+      aRv.ThrowNotFoundError(nsPrintfCString(
+          "Could not find actor constructor '%s'", 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;
     }
@@ -94,36 +93,124 @@ already_AddRefed<JSActor> JSActorManager
   RefPtr<JSActor> actor = InitJSActor(actorObj, aName, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   mJSActors.Put(aName, RefPtr{actor});
   return actor.forget();
 }
 
+#define CHILD_DIAGNOSTIC_ASSERT(test, msg) \
+  do {                                     \
+    if (XRE_IsParentProcess()) {           \
+      MOZ_ASSERT(test, msg);               \
+    } else {                               \
+      MOZ_DIAGNOSTIC_ASSERT(test, msg);    \
+    }                                      \
+  } while (0)
+
+void JSActorManager::ReceiveRawMessage(const JSActorMessageMeta& aMetadata,
+                                       ipc::StructuredCloneData&& aData,
+                                       ipc::StructuredCloneData&& aStack) {
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+  CrashReporter::AutoAnnotateCrashReport autoActorName(
+      CrashReporter::Annotation::JSActorName, aMetadata.actorName());
+  CrashReporter::AutoAnnotateCrashReport autoMessageName(
+      CrashReporter::Annotation::JSActorMessage,
+      NS_LossyConvertUTF16toASCII(aMetadata.messageName()));
+
+  // We're going to be running JS. Enter the privileged junk realm so we can set
+  // up our JS state correctly.
+  AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSActor message handler");
+  JSContext* cx = aes.cx();
+
+  // Ensure any errors reported to `error` are set on the scope, so they're
+  // reported.
+  ErrorResult error;
+  auto autoSetException =
+      MakeScopeExit([&] { Unused << error.MaybeSetPendingException(cx); });
+
+  // If an async stack was provided, set up our async stack state.
+  JS::Rooted<JSObject*> stack(cx);
+  Maybe<JS::AutoSetAsyncStackForNewCalls> stackSetter;
+  {
+    JS::Rooted<JS::Value> stackVal(cx);
+    aStack.Read(cx, &stackVal, error);
+    if (error.Failed()) {
+      return;
+    }
+
+    if (stackVal.isObject()) {
+      stack = &stackVal.toObject();
+      if (!js::IsSavedFrame(stack)) {
+        CHILD_DIAGNOSTIC_ASSERT(false, "Stack must be a SavedFrame object");
+        error.ThrowDataError("Actor async stack must be a SavedFrame object");
+        return;
+      }
+      stackSetter.emplace(cx, stack, "JSActor query");
+    }
+  }
+
+  RefPtr<JSActor> actor = GetActor(aMetadata.actorName(), error);
+  if (error.Failed()) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> data(cx);
+  aData.Read(cx, &data, error);
+  if (error.Failed()) {
+    CHILD_DIAGNOSTIC_ASSERT(false, "Should not receive non-decodable data");
+    return;
+  }
+
+  switch (aMetadata.kind()) {
+    case JSActorMessageKind::QueryResolve:
+    case JSActorMessageKind::QueryReject:
+      actor->ReceiveQueryReply(cx, aMetadata, data, error);
+      break;
+
+    case JSActorMessageKind::Message:
+    case JSActorMessageKind::Query:
+      actor->ReceiveMessageOrQuery(cx, aMetadata, data, error);
+      break;
+
+    default:
+      MOZ_ASSERT_UNREACHABLE();
+  }
+}
+
 void JSActorManager::JSActorWillDestroy() {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+  CrashReporter::AutoAnnotateCrashReport autoMessageName(
+      CrashReporter::Annotation::JSActorMessage, "<WillDestroy>"_ns);
 
   // 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) {
+    CrashReporter::AutoAnnotateCrashReport autoActorName(
+        CrashReporter::Annotation::JSActorName, actor->Name());
     actor->StartDestroy();
   }
 }
 
 void JSActorManager::JSActorDidDestroy() {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+  CrashReporter::AutoAnnotateCrashReport autoMessageName(
+      CrashReporter::Annotation::JSActorMessage, "<DidDestroy>"_ns);
 
   // 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) {
+    CrashReporter::AutoAnnotateCrashReport autoActorName(
+        CrashReporter::Annotation::JSActorName, entry.GetData()->Name());
     entry.GetData()->AfterDestroy();
   }
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/jsactor/JSActorManager.h
+++ b/dom/ipc/jsactor/JSActorManager.h
@@ -27,16 +27,23 @@ class JSActorManager : public nsISupport
   /**
    * 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);
 
+  /**
+   * Handle receiving a raw message from the other side.
+   */
+  void ReceiveRawMessage(const JSActorMessageMeta& aMetadata,
+                         ipc::StructuredCloneData&& aData,
+                         ipc::StructuredCloneData&& aStack);
+
  protected:
   /**
    * Lifecycle methods which will fire the `willDestroy` and `didDestroy`
    * methods on relevant actors.
    */
   void JSActorWillDestroy();
   void JSActorDidDestroy();
 
--- a/dom/ipc/jsactor/JSProcessActorChild.cpp
+++ b/dom/ipc/jsactor/JSProcessActorChild.cpp
@@ -23,49 +23,16 @@ NS_INTERFACE_MAP_END_INHERITING(JSActor)
 NS_IMPL_ADDREF_INHERITED(JSProcessActorChild, JSActor)
 NS_IMPL_RELEASE_INHERITED(JSProcessActorChild, JSActor)
 
 JSObject* JSProcessActorChild::WrapObject(JSContext* aCx,
                                           JS::Handle<JSObject*> aGivenProto) {
   return JSProcessActorChild_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-namespace {
-
-class AsyncMessageToProcessParent : public Runnable {
- public:
-  AsyncMessageToProcessParent(const JSActorMessageMeta& aMetadata,
-                              ipc::StructuredCloneData&& aData,
-                              ipc::StructuredCloneData&& aStack)
-      : mozilla::Runnable("InProcessParent::HandleAsyncMessage"),
-        mMetadata(aMetadata),
-        mData(std::move(aData)),
-        mStack(std::move(aStack)) {}
-
-  NS_IMETHOD Run() override {
-    MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
-    if (auto* parent = InProcessParent::Singleton()) {
-      RefPtr<JSProcessActorParent> actor;
-      parent->GetActor(mMetadata.actorName(), getter_AddRefs(actor));
-      if (actor) {
-        actor->ReceiveRawMessage(mMetadata, std::move(mData),
-                                 std::move(mStack));
-      }
-    }
-    return NS_OK;
-  }
-
- private:
-  JSActorMessageMeta mMetadata;
-  ipc::StructuredCloneData mData;
-  ipc::StructuredCloneData mStack;
-};
-
-}  // namespace
-
 void JSProcessActorChild::SendRawMessage(const JSActorMessageMeta& aMeta,
                                          ipc::StructuredCloneData&& aData,
                                          ipc::StructuredCloneData&& aStack,
                                          ErrorResult& aRv) {
   if (NS_WARN_IF(!CanSend() || !mManager || !mManager->GetCanSend())) {
     aRv.ThrowInvalidStateError("JSProcessActorChild cannot send at the moment");
     return;
   }
@@ -78,19 +45,19 @@ void JSProcessActorChild::SendRawMessage
                         PromiseFlatCString(aMeta.actorName()).get()));
     return;
   }
 
   // If the parent side is in the same process, we have a PInProcess manager,
   // and can dispatch the message directly to the event loop.
   ContentChild* contentChild = mManager->AsContentChild();
   if (!contentChild) {
-    MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
-    NS_DispatchToMainThread(MakeAndAddRef<AsyncMessageToProcessParent>(
-        aMeta, std::move(aData), std::move(aStack)));
+    SendRawMessageInProcess(aMeta, std::move(aData), std::move(aStack), []() {
+      return do_AddRef(InProcessParent::Singleton());
+    });
     return;
   }
 
   ClonedMessageData msgData;
   ClonedMessageData stackData;
   if (NS_WARN_IF(
           !aData.BuildClonedMessageDataForChild(contentChild, msgData)) ||
       NS_WARN_IF(
--- a/dom/ipc/jsactor/JSProcessActorParent.cpp
+++ b/dom/ipc/jsactor/JSProcessActorParent.cpp
@@ -34,49 +34,16 @@ void JSProcessActorParent::Init(const ns
   SetName(aName);
   mManager = aManager;
 
   InvokeCallback(CallbackFunction::ActorCreated);
 }
 
 JSProcessActorParent::~JSProcessActorParent() { MOZ_ASSERT(!mManager); }
 
-namespace {
-
-class AsyncMessageToProcessChild : public Runnable {
- public:
-  AsyncMessageToProcessChild(const JSActorMessageMeta& aMetadata,
-                             ipc::StructuredCloneData&& aData,
-                             ipc::StructuredCloneData&& aStack)
-      : mozilla::Runnable("InProcessChild::HandleAsyncMessage"),
-        mMetadata(aMetadata),
-        mData(std::move(aData)),
-        mStack(std::move(aStack)) {}
-
-  NS_IMETHOD Run() override {
-    MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
-    if (auto* child = InProcessChild::Singleton()) {
-      RefPtr<JSProcessActorChild> actor;
-      child->GetActor(mMetadata.actorName(), getter_AddRefs(actor));
-      if (actor) {
-        actor->ReceiveRawMessage(mMetadata, std::move(mData),
-                                 std::move(mStack));
-      }
-    }
-    return NS_OK;
-  }
-
- private:
-  JSActorMessageMeta mMetadata;
-  ipc::StructuredCloneData mData;
-  ipc::StructuredCloneData mStack;
-};
-
-}  // namespace
-
 void JSProcessActorParent::SendRawMessage(const JSActorMessageMeta& aMeta,
                                           ipc::StructuredCloneData&& aData,
                                           ipc::StructuredCloneData&& aStack,
                                           ErrorResult& aRv) {
   if (NS_WARN_IF(!CanSend() || !mManager || !mManager->GetCanSend())) {
     aRv.ThrowInvalidStateError(
         nsPrintfCString("Actor '%s' cannot send message '%s' during shutdown.",
                         PromiseFlatCString(aMeta.actorName()).get(),
@@ -92,18 +59,19 @@ void JSProcessActorParent::SendRawMessag
         NS_ConvertUTF16toUTF8(aMeta.messageName()).get()));
     return;
   }
 
   // If the parent side is in the same process, we have a PInProcess manager,
   // and can dispatch the message directly to the event loop.
   ContentParent* contentParent = mManager->AsContentParent();
   if (!contentParent) {
-    NS_DispatchToMainThread(MakeAndAddRef<AsyncMessageToProcessChild>(
-        aMeta, std::move(aData), std::move(aStack)));
+    SendRawMessageInProcess(aMeta, std::move(aData), std::move(aStack), []() {
+      return do_AddRef(InProcessChild::Singleton());
+    });
     return;
   }
 
   // Cross-process case - send data over ContentParent to other side.
   ClonedMessageData msgData;
   ClonedMessageData stackData;
   if (NS_WARN_IF(
           !aData.BuildClonedMessageDataForParent(contentParent, msgData)) ||
--- a/dom/ipc/jsactor/JSWindowActorChild.cpp
+++ b/dom/ipc/jsactor/JSWindowActorChild.cpp
@@ -30,61 +30,29 @@ void JSWindowActorChild::Init(const nsAC
                               WindowGlobalChild* aManager) {
   MOZ_ASSERT(!mManager, "Cannot Init() a JSWindowActorChild twice!");
   SetName(aName);
   mManager = aManager;
 
   InvokeCallback(CallbackFunction::ActorCreated);
 }
 
-namespace {
-
-class AsyncMessageToParent : public Runnable {
- public:
-  AsyncMessageToParent(const JSActorMessageMeta& aMetadata,
-                       ipc::StructuredCloneData&& aData,
-                       ipc::StructuredCloneData&& aStack,
-                       WindowGlobalChild* aManager)
-      : mozilla::Runnable("WindowGlobalParent::HandleAsyncMessage"),
-        mMetadata(aMetadata),
-        mData(std::move(aData)),
-        mStack(std::move(aStack)),
-        mManager(aManager) {}
-
-  NS_IMETHOD Run() override {
-    MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
-    RefPtr<WindowGlobalParent> parent = mManager->GetParentActor();
-    if (parent) {
-      parent->ReceiveRawMessage(mMetadata, std::move(mData), std::move(mStack));
-    }
-    return NS_OK;
-  }
-
- private:
-  JSActorMessageMeta mMetadata;
-  ipc::StructuredCloneData mData;
-  ipc::StructuredCloneData mStack;
-  RefPtr<WindowGlobalChild> mManager;
-};
-
-}  // anonymous namespace
-
 void JSWindowActorChild::SendRawMessage(const JSActorMessageMeta& aMeta,
                                         ipc::StructuredCloneData&& aData,
                                         ipc::StructuredCloneData&& aStack,
                                         ErrorResult& aRv) {
   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());
+    SendRawMessageInProcess(
+        aMeta, std::move(aData), std::move(aStack),
+        [manager{mManager}]() { return manager->GetParentActor(); });
     return;
   }
 
   if (NS_WARN_IF(
           !AllowMessage(aMeta, aData.DataLength() + aStack.DataLength()))) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
--- a/dom/ipc/jsactor/JSWindowActorParent.cpp
+++ b/dom/ipc/jsactor/JSWindowActorParent.cpp
@@ -69,19 +69,19 @@ void JSWindowActorParent::SendRawMessage
                                          ipc::StructuredCloneData&& aStack,
                                          ErrorResult& aRv) {
   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());
+    SendRawMessageInProcess(
+        aMeta, std::move(aData), std::move(aStack),
+        [manager{mManager}]() { return manager->GetChildActor(); });
     return;
   }
 
   if (NS_WARN_IF(
           !AllowMessage(aMeta, aData.DataLength() + aStack.DataLength()))) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }