Bug 1513877 - Implement send and receive methods for base WindowActor class. r=nika
authorJohn Dai <jdai@mozilla.com>
Fri, 08 Feb 2019 13:02:08 +0000
changeset 516121 b482c6618d72
parent 516120 546288d07c7c
child 516122 8ec307e6000f
child 516196 084e2c76a59b
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1513877
milestone67.0a1
first release with
nightly linux64
b482c6618d72 / 67.0a1 / 20190208161559 / files
nightly mac
b482c6618d72 / 67.0a1 / 20190208161559 / files
nightly win32
b482c6618d72 / 67.0a1 / 20190208161559 / files
nightly win64
b482c6618d72 / 67.0a1 / 20190208161559 / files
nightly linux32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1513877 - Implement send and receive methods for base WindowActor class. r=nika Differential Revision: https://phabricator.services.mozilla.com/D18695
dom/base/nsFrameMessageManager.cpp
dom/base/nsFrameMessageManager.h
dom/chrome-webidl/JSWindowActor.webidl
dom/ipc/JSWindowActorChild.cpp
dom/ipc/JSWindowActorChild.h
dom/ipc/JSWindowActorParent.cpp
dom/ipc/JSWindowActorParent.h
dom/ipc/JSWindowActorService.cpp
dom/ipc/JSWindowActorService.h
dom/ipc/PWindowGlobal.ipdl
dom/ipc/WindowGlobalChild.cpp
dom/ipc/WindowGlobalChild.h
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
dom/ipc/tests/browser_JSWindowActor.js
dom/ipc/tests/test_JSWindowActor.xul
toolkit/actors/TestChild.jsm
toolkit/actors/TestParent.jsm
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -375,19 +375,21 @@ void nsFrameMessageManager::GetDelayedSc
     }
 
     nsTArray<JS::Value>* array = aList.AppendElement(2);
     array->AppendElement(url);
     array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
   }
 }
 
-static bool GetParamsForMessage(JSContext* aCx, const JS::Value& aValue,
-                                const JS::Value& aTransfer,
-                                StructuredCloneData& aData) {
+/* static */
+bool nsFrameMessageManager::GetParamsForMessage(JSContext* aCx,
+                                                const JS::Value& aValue,
+                                                const JS::Value& aTransfer,
+                                                StructuredCloneData& aData) {
   // First try to use structured clone on the whole thing.
   JS::RootedValue v(aCx, aValue);
   JS::RootedValue t(aCx, aTransfer);
   ErrorResult rv;
   aData.Write(aCx, v, t, rv);
   if (!rv.Failed()) {
     return true;
   }
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -272,16 +272,20 @@ class nsFrameMessageManager : public nsI
   static mozilla::dom::ChildProcessMessageManager* GetChildProcessManager() {
     return sChildProcessManager;
   }
   static void SetChildProcessManager(
       mozilla::dom::ChildProcessMessageManager* aManager) {
     sChildProcessManager = aManager;
   }
 
+  static bool GetParamsForMessage(JSContext* aCx, const JS::Value& aValue,
+                                  const JS::Value& aTransfer,
+                                  StructuredCloneData& aData);
+
   void SetInitialProcessData(JS::HandleValue aInitialData);
 
   void LoadPendingScripts();
 
  protected:
   friend class MMListenerRemover;
 
   virtual mozilla::dom::MessageBroadcaster* GetParentManager() {
--- a/dom/chrome-webidl/JSWindowActor.webidl
+++ b/dom/chrome-webidl/JSWindowActor.webidl
@@ -1,16 +1,26 @@
 /* -*- 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/. */
 
+[NoInterfaceObject]
+interface JSWindowActor {
+  [Throws]
+  void sendAsyncMessage(DOMString actorName,
+                        DOMString messageName,
+                        optional any obj,
+                        optional any transfers);
+};
+
 [ChromeOnly, ChromeConstructor]
 interface JSWindowActorParent {
   readonly attribute WindowGlobalParent manager;
 };
-
+JSWindowActorParent implements JSWindowActor;
 
 [ChromeOnly, ChromeConstructor]
 interface JSWindowActorChild {
   readonly attribute WindowGlobalChild manager;
-};
\ No newline at end of file
+};
+JSWindowActorChild implements JSWindowActor;
\ No newline at end of file
--- a/dom/ipc/JSWindowActorChild.cpp
+++ b/dom/ipc/JSWindowActorChild.cpp
@@ -2,16 +2,18 @@
 /* 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/JSWindowActorBinding.h"
 #include "mozilla/dom/JSWindowActorChild.h"
 #include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/MessageManagerBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 JSObject* JSWindowActorChild::WrapObject(JSContext* aCx,
                                          JS::Handle<JSObject*> aGivenProto) {
   return JSWindowActorChild_Binding::Wrap(aCx, this, aGivenProto);
 }
@@ -22,15 +24,93 @@ void JSWindowActorChild::Init(WindowGlob
   MOZ_ASSERT(!mManager, "Cannot Init() a JSWindowActorChild twice!");
   mManager = aManager;
 }
 
 nsISupports* JSWindowActorChild::GetParentObject() const {
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
+namespace {
+
+class AsyncMessageToParent : public Runnable {
+ public:
+  AsyncMessageToParent(const nsAString& aActorName,
+                       const nsAString& aMessageName,
+                       ipc::StructuredCloneData&& aData,
+                       WindowGlobalParent* aParent)
+      : mozilla::Runnable("WindowGlobalParent::HandleAsyncMessage"),
+        mActorName(aActorName),
+        mMessageName(aMessageName),
+        mData(std::move(aData)),
+        mParent(aParent) {}
+
+  NS_IMETHOD Run() override {
+    MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
+    mParent->HandleAsyncMessage(mActorName, mMessageName, mData);
+    return NS_OK;
+  }
+
+ private:
+  nsString mActorName;
+  nsString mMessageName;
+  ipc::StructuredCloneData mData;
+  RefPtr<WindowGlobalParent> mParent;
+};
+
+}  // anonymous namespace
+
+void JSWindowActorChild::SendAsyncMessage(JSContext* aCx,
+                                          const nsAString& aActorName,
+                                          const nsAString& aMessageName,
+                                          JS::Handle<JS::Value> aObj,
+                                          JS::Handle<JS::Value> aTransfers,
+                                          ErrorResult& aRv) {
+  // If we've closed our channel already, just raise an exception.
+  if (NS_WARN_IF(mManager->IsClosed())) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  // Serialize our object out to a StructuredCloneData.
+  ipc::StructuredCloneData data;
+  if (!aObj.isUndefined() && !nsFrameMessageManager::GetParamsForMessage(
+                                 aCx, aObj, aTransfers, data)) {
+    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
+  // If we're an in-process, asynchronously fire off the callback for in-process
+  // loads.
+  if (mManager->IsInProcess()) {
+    RefPtr<WindowGlobalParent> parent = mManager->GetParentActor();
+    RefPtr<AsyncMessageToParent> ev = new AsyncMessageToParent(
+        aActorName, aMessageName, std::move(data), parent);
+    DebugOnly<nsresult> rv = NS_DispatchToMainThread(ev);
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "JS Window Actor AsyncMessageToParent dispatch failed");
+    return;
+  }
+
+  // If we're a cross-process, send the async message over the corresponding
+  // actor.
+  ClonedMessageData msgData;
+  nsIContentChild* cc = ContentChild::GetSingleton();
+  if (!data.BuildClonedMessageDataForChild(cc, msgData)) {
+    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
+  if (!mManager->SendAsyncMessage(PromiseFlatString(aActorName),
+                                  PromiseFlatString(aMessageName), msgData)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(JSWindowActorChild, mManager)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSWindowActorChild, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(JSWindowActorChild, Release)
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/JSWindowActorChild.h
+++ b/dom/ipc/JSWindowActorChild.h
@@ -41,16 +41,20 @@ class JSWindowActorChild final : public 
 
   static already_AddRefed<JSWindowActorChild> Constructor(GlobalObject& aGlobal,
                                                           ErrorResult& aRv) {
     return MakeAndAddRef<JSWindowActorChild>();
   }
 
   WindowGlobalChild* Manager() const;
   void Init(WindowGlobalChild* aManager);
+  void SendAsyncMessage(JSContext* aCx, const nsAString& aActorName,
+                        const nsAString& aMessageName,
+                        JS::Handle<JS::Value> aObj,
+                        JS::Handle<JS::Value> aTransfers, ErrorResult& aRv);
 
  private:
   RefPtr<WindowGlobalChild> mManager;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/ipc/JSWindowActorParent.cpp
+++ b/dom/ipc/JSWindowActorParent.cpp
@@ -2,16 +2,17 @@
 /* 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/JSWindowActorBinding.h"
 #include "mozilla/dom/JSWindowActorParent.h"
 #include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/MessageManagerBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 JSObject* JSWindowActorParent::WrapObject(JSContext* aCx,
                                           JS::Handle<JSObject*> aGivenProto) {
   return JSWindowActorParent_Binding::Wrap(aCx, this, aGivenProto);
 }
@@ -22,15 +23,93 @@ void JSWindowActorParent::Init(WindowGlo
   MOZ_ASSERT(!mManager, "Cannot Init() a JSWindowActorParent twice!");
   mManager = aManager;
 }
 
 nsISupports* JSWindowActorParent::GetParentObject() const {
   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 }
 
+namespace {
+
+class AsyncMessageToChild : public Runnable {
+ public:
+  AsyncMessageToChild(const nsAString& aActorName,
+                      const nsAString& aMessageName,
+                      ipc::StructuredCloneData&& aData,
+                      WindowGlobalChild* aChild)
+      : mozilla::Runnable("WindowGlobalChild::HandleAsyncMessage"),
+        mActorName(aActorName),
+        mMessageName(aMessageName),
+        mData(std::move(aData)),
+        mChild(aChild) {}
+
+  NS_IMETHOD Run() override {
+    MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread.");
+    mChild->HandleAsyncMessage(mActorName, mMessageName, mData);
+    return NS_OK;
+  }
+
+ private:
+  nsString mActorName;
+  nsString mMessageName;
+  ipc::StructuredCloneData mData;
+  RefPtr<WindowGlobalChild> mChild;
+};
+
+}  // anonymous namespace
+
+void JSWindowActorParent::SendAsyncMessage(JSContext* aCx,
+                                           const nsAString& aActorName,
+                                           const nsAString& aMessageName,
+                                           JS::Handle<JS::Value> aObj,
+                                           JS::Handle<JS::Value> aTransfers,
+                                           ErrorResult& aRv) {
+  // If we've closed our channel already, just raise a warning.
+  if (NS_WARN_IF(mManager->IsClosed())) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  // Serialize our object out to a StructuredCloneData.
+  ipc::StructuredCloneData data;
+  if (!aObj.isUndefined() && !nsFrameMessageManager::GetParamsForMessage(
+                                 aCx, aObj, aTransfers, data)) {
+    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
+  // If we're an in-process, asynchronously fire off the callback for in-process
+  // loads.
+  if (mManager->IsInProcess()) {
+    RefPtr<WindowGlobalChild> child = mManager->GetChildActor();
+    RefPtr<AsyncMessageToChild> ev = new AsyncMessageToChild(
+        aActorName, aMessageName, std::move(data), child);
+    DebugOnly<nsresult> rv = NS_DispatchToMainThread(ev);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                         "JS Window Actor AsyncMessageToChild dispatch failed");
+    return;
+  }
+
+  // If we're a cross-process, send the async message over the corresponding
+  // actor.
+  ClonedMessageData msgData;
+  RefPtr<TabParent> tabParent = mManager->GetTabParent();
+  nsIContentParent* cp = tabParent->Manager();
+  if (!data.BuildClonedMessageDataForParent(cp, msgData)) {
+    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
+  if (!mManager->SendAsyncMessage(PromiseFlatString(aActorName),
+                                  PromiseFlatString(aMessageName), msgData)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(JSWindowActorParent, mManager)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSWindowActorParent, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(JSWindowActorParent, Release)
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/ipc/JSWindowActorParent.h
+++ b/dom/ipc/JSWindowActorParent.h
@@ -41,16 +41,20 @@ class JSWindowActorParent final : public
 
   static already_AddRefed<JSWindowActorParent> Constructor(
       GlobalObject& aGlobal, ErrorResult& aRv) {
     return MakeAndAddRef<JSWindowActorParent>();
   }
 
   WindowGlobalParent* Manager() const;
   void Init(WindowGlobalParent* aManager);
+  void SendAsyncMessage(JSContext* aCx, const nsAString& aActorName,
+                        const nsAString& aMessageName,
+                        JS::Handle<JS::Value> aObj,
+                        JS::Handle<JS::Value> aTransfers, ErrorResult& aRv);
 
  private:
   RefPtr<WindowGlobalParent> mManager;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/ipc/JSWindowActorService.cpp
+++ b/dom/ipc/JSWindowActorService.cpp
@@ -135,10 +135,50 @@ void JSWindowActorService::ConstructActo
 
   // Invoke the constructor loaded from the module.
   if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), aActor)) {
     aRv.NoteJSContextException(cx);
     return;
   }
 }
 
+void JSWindowActorService::ReceiveMessage(JS::RootedObject& aObj,
+                                          const nsString& aMessageName,
+                                          ipc::StructuredCloneData& aData) {
+  IgnoredErrorResult error;
+  AutoEntryScript aes(js::CheckedUnwrap(aObj),
+                      "WindowGlobalChild Message Handler");
+  JSContext* cx = aes.cx();
+
+  // We passed the unwrapped object to AutoEntryScript so we now need to
+  // enter the realm of the global object that represents the realm of our
+  // callback.
+  JSAutoRealm ar(cx, aObj);
+  JS::RootedValue json(cx, JS::NullValue());
+
+  // Deserialize our data into a JS object in the correct compartment.
+  aData.Read(aes.cx(), &json, error);
+  if (NS_WARN_IF(error.Failed())) {
+    JS_ClearPendingException(cx);
+    return;
+  }
+
+  RootedDictionary<ReceiveMessageArgument> argument(cx);
+  argument.mObjects = JS_NewPlainObject(cx);
+  argument.mName = aMessageName;
+  argument.mData = json;
+  argument.mJson = json;
+  JS::RootedValue argv(cx);
+  if (NS_WARN_IF(!ToJSValue(cx, argument, &argv))) {
+    return;
+  }
+
+  // Now that we have finished, call the recvAsyncMessage callback.
+  JS::RootedValue dummy(cx);
+  if (NS_WARN_IF(!JS_CallFunctionName(cx, aObj, "recvAsyncMessage",
+                                      JS::HandleValueArray(argv), &dummy))) {
+    JS_ClearPendingException(cx);
+    return;
+  }
+}
+
 }  // namespace dom
 }  // namespace mozilla
\ No newline at end of file
--- a/dom/ipc/JSWindowActorService.h
+++ b/dom/ipc/JSWindowActorService.h
@@ -35,16 +35,19 @@ class JSWindowActorService final {
   void GetJSWindowActorInfos(nsTArray<JSWindowActorInfo>& aInfos);
 
   // 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 nsAString& aName, bool aParentSide,
                       JS::MutableHandleObject aActor, ErrorResult& aRv);
 
+  void ReceiveMessage(JS::RootedObject& aObj, const nsString& aMessageName,
+                      ipc::StructuredCloneData& aData);
+
  private:
   JSWindowActorService();
   ~JSWindowActorService();
 
   nsClassHashtable<nsStringHashKey, WindowActorOptions> mDescriptors;
 };
 
 }  // namespace dom
--- a/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -21,16 +21,19 @@ namespace dom {
  */
 async protocol PWindowGlobal
 {
   manager PBrowser or PInProcess;
 
 child:
   async __delete__();
 
+both:
+  async AsyncMessage(nsString aActorName, nsString aMessage, ClonedMessageData aData);
+
 parent:
   /// Update the URI of the document in this WindowGlobal.
   async UpdateDocumentURI(nsIURI aUri);
 
   /// Notify the parent that this PWindowGlobal is now the current global.
   async BecomeCurrentWindowGlobal();
 
   async Destroy();
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -107,16 +107,55 @@ void WindowGlobalChild::Destroy() {
   RefPtr<TabChild> tabChild = GetTabChild();
   if (!tabChild || !tabChild->IsDestroyed()) {
     SendDestroy();
   }
 
   mIPCClosed = true;
 }
 
+IPCResult WindowGlobalChild::RecvAsyncMessage(const nsString& aActorName,
+                                              const nsString& aMessageName,
+                                              const ClonedMessageData& aData) {
+  StructuredCloneData data;
+  data.BorrowFromClonedMessageDataForChild(aData);
+  HandleAsyncMessage(aActorName, aMessageName, data);
+  return IPC_OK();
+}
+
+void WindowGlobalChild::HandleAsyncMessage(const nsString& aActorName,
+                                           const nsString& aMessageName,
+                                           StructuredCloneData& aData) {
+  if (NS_WARN_IF(mIPCClosed)) {
+    return;
+  }
+
+  // Force creation of the actor if it hasn't been created yet.
+  IgnoredErrorResult rv;
+  RefPtr<JSWindowActorChild> actor = GetActor(aActorName, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return;
+  }
+
+  // Get the JSObject for the named actor.
+  JS::RootedObject obj(RootingCx(), actor->GetWrapper());
+  if (NS_WARN_IF(!obj)) {
+    // If we don't have a preserved wrapper, there won't be any receiver
+    // method to call.
+    return;
+  }
+
+  RefPtr<JSWindowActorService> actorSvc = JSWindowActorService::GetSingleton();
+  if (NS_WARN_IF(!actorSvc)) {
+    return;
+  }
+
+  actorSvc->ReceiveMessage(obj, aMessageName, aData);
+}
+
 already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
     const nsAString& aName, ErrorResult& aRv) {
   // Check if this actor has already been created, and return it if it has.
   if (mWindowActors.Contains(aName)) {
     return do_AddRef(mWindowActors.GetWeak(aName));
   }
 
   // Otherwise, we want to create a new instance of this actor. Call into the
@@ -166,9 +205,9 @@ nsISupports* WindowGlobalChild::GetParen
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild, mWindowGlobal,
                                       mBrowsingContext, mWindowActors)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WindowGlobalChild, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WindowGlobalChild, Release)
 
 }  // namespace dom
-}  // namespace mozilla
+}  // namespace mozilla
\ No newline at end of file
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -21,16 +21,18 @@ class BrowsingContext;
 class WindowGlobalParent;
 class JSWindowActorChild;
 
 /**
  * Actor for a single nsGlobalWindowInner. This actor is used to communicate
  * information to the parent process asynchronously.
  */
 class WindowGlobalChild : public nsWrapperCache, public PWindowGlobalChild {
+  friend class PWindowGlobalChild;
+
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WindowGlobalChild)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WindowGlobalChild)
 
   static already_AddRefed<WindowGlobalChild> GetByInnerWindowId(
       uint64_t aInnerWindowId);
 
   static already_AddRefed<WindowGlobalChild> GetByInnerWindowId(
@@ -58,29 +60,38 @@ class WindowGlobalChild : public nsWrapp
   // 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<TabChild> GetTabChild();
 
+  void HandleAsyncMessage(const nsString& aActorName,
+                          const nsString& aMessageName,
+                          ipc::StructuredCloneData& aData);
+
   // Get a JS actor object by name.
   already_AddRefed<JSWindowActorChild> GetActor(const nsAString& aName,
                                                 ErrorResult& aRv);
 
   // Create and initialize the WindowGlobalChild object.
   static already_AddRefed<WindowGlobalChild> Create(
       nsGlobalWindowInner* aWindow);
 
   nsISupports* GetParentObject();
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
  protected:
+  // IPC messages
+  mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aActorName,
+                                           const nsString& aMessage,
+                                           const ClonedMessageData& aData);
+
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
   WindowGlobalChild(nsGlobalWindowInner* aWindow, dom::BrowsingContext* aBc);
   ~WindowGlobalChild();
 
   RefPtr<nsGlobalWindowInner> mWindowGlobal;
   RefPtr<dom::BrowsingContext> mBrowsingContext;
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -148,29 +148,53 @@ IPCResult WindowGlobalParent::RecvDestro
     RefPtr<TabParent> tabParent = GetTabParent();
     if (!tabParent || !tabParent->IsDestroyed()) {
       Unused << Send__delete__(this);
     }
   }
   return IPC_OK();
 }
 
-bool WindowGlobalParent::IsCurrentGlobal() {
-  return !mIPCClosed && mBrowsingContext->GetCurrentWindowGlobal() == this;
+IPCResult WindowGlobalParent::RecvAsyncMessage(const nsString& aActorName,
+                                               const nsString& aMessageName,
+                                               const ClonedMessageData& aData) {
+  StructuredCloneData data;
+  data.BorrowFromClonedMessageDataForParent(aData);
+  HandleAsyncMessage(aActorName, aMessageName, data);
+  return IPC_OK();
 }
 
-void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
-  mIPCClosed = true;
-  gWindowGlobalParentsById->Remove(mInnerWindowId);
-  mBrowsingContext->UnregisterWindowGlobal(this);
+void WindowGlobalParent::HandleAsyncMessage(const nsString& aActorName,
+                                            const nsString& aMessageName,
+                                            StructuredCloneData& aData) {
+  if (NS_WARN_IF(mIPCClosed)) {
+    return;
+  }
+
+  // Force creation of the actor if it hasn't been created yet.
+  IgnoredErrorResult rv;
+  RefPtr<JSWindowActorParent> actor = GetActor(aActorName, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return;
+  }
 
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->NotifyObservers(this, "window-global-destroyed", nullptr);
+  // Get the JSObject for the named actor.
+  JS::RootedObject obj(RootingCx(), actor->GetWrapper());
+  if (NS_WARN_IF(!obj)) {
+    // If we don't have a preserved wrapper, there won't be any receiver
+    // method to call.
+    return;
   }
+
+  RefPtr<JSWindowActorService> actorSvc = JSWindowActorService::GetSingleton();
+  if (NS_WARN_IF(!actorSvc)) {
+    return;
+  }
+
+  actorSvc->ReceiveMessage(obj, aMessageName, aData);
 }
 
 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
     const nsAString& aName, ErrorResult& aRv) {
   // Check if this actor has already been created, and return it if it has.
   if (mWindowActors.Contains(aName)) {
     return do_AddRef(mWindowActors.GetWeak(aName));
   }
@@ -196,16 +220,31 @@ already_AddRefed<JSWindowActorParent> Wi
 
   MOZ_RELEASE_ASSERT(!actor->Manager(),
                      "mManager was already initialized once!");
   actor->Init(this);
   mWindowActors.Put(aName, actor);
   return actor.forget();
 }
 
+bool WindowGlobalParent::IsCurrentGlobal() {
+  return !mIPCClosed && mBrowsingContext->GetCurrentWindowGlobal() == this;
+}
+
+void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
+  mIPCClosed = true;
+  gWindowGlobalParentsById->Remove(mInnerWindowId);
+  mBrowsingContext->UnregisterWindowGlobal(this);
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs) {
+    obs->NotifyObservers(this, "window-global-destroyed", nullptr);
+  }
+}
+
 WindowGlobalParent::~WindowGlobalParent() {
   MOZ_ASSERT(!gWindowGlobalParentsById ||
              !gWindowGlobalParentsById->Contains(mInnerWindowId));
 }
 
 JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
                                          JS::Handle<JSObject*> aGivenProto) {
   return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -57,16 +57,20 @@ class WindowGlobalParent final : public 
   // Get a JS actor object by name.
   already_AddRefed<JSWindowActorParent> GetActor(const nsAString& aName,
                                                  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<TabParent> GetTabParent();
 
+  void HandleAsyncMessage(const nsString& aActorName,
+                          const nsString& aMessageName,
+                          ipc::StructuredCloneData& aData);
+
   // 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; }
 
   // The BrowsingContext which this WindowGlobal has been loaded into.
   CanonicalBrowsingContext* BrowsingContext() { return mBrowsingContext; }
 
@@ -97,16 +101,19 @@ class WindowGlobalParent final : public 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
  protected:
   // IPC messages
   mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI);
   mozilla::ipc::IPCResult RecvBecomeCurrentWindowGlobal();
   mozilla::ipc::IPCResult RecvDestroy();
+  mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aActorName,
+                                           const nsString& aMessageName,
+                                           const ClonedMessageData& aData);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
   ~WindowGlobalParent();
 
   // NOTE: This document principal doesn't reflect possible |document.domain|
   // mutations which may have been made in the actual document.
--- a/dom/ipc/tests/browser_JSWindowActor.js
+++ b/dom/ipc/tests/browser_JSWindowActor.js
@@ -16,30 +16,86 @@ add_task(function test_registerWindowAct
   ok(ChromeUtils, "Should be able to get the ChromeUtils interface");
   ChromeUtils.registerWindowActor("Test", windowActorOptions);
   SimpleTest.doesThrow(() =>
     ChromeUtils.registerWindowActor("Test", windowActorOptions),
     "Should throw if register has duplicate name.");
   ChromeUtils.unregisterWindowActor("Test");
 });
 
-add_task(async function() {
+add_task(async function test_getActor() {
   await BrowserTestUtils.withNewTab({gBrowser, url: URL},
     async function(browser) {
       ChromeUtils.registerWindowActor("Test", windowActorOptions);
       let parent = browser.browsingContext.currentWindowGlobal;
-      isnot(parent, null, "WindowGlobalParent should have value.");
+      ok(parent, "WindowGlobalParent should have value.");
       let actorParent = parent.getActor("Test");
       is(actorParent.show(), "TestParent", "actor show should have vaule.");
       is(actorParent.manager, parent, "manager should match WindowGlobalParent.");
 
       await ContentTask.spawn(
         browser, {}, async function() {
           let child = content.window.getWindowGlobalChild();
-          isnot(child, null, "WindowGlobalChild should have value.");
+          ok(child, "WindowGlobalChild should have value.");
           is(child.isInProcess, false, "Actor should be loaded in the content process.");
           let actorChild = child.getActor("Test");
           is(actorChild.show(), "TestChild", "actor show should have vaule.");
           is(actorChild.manager, child, "manager should match WindowGlobalChild.");
         });
       ChromeUtils.unregisterWindowActor("Test");
     });
 });
+
+add_task(async function test_asyncMessage() {
+  await BrowserTestUtils.withNewTab({gBrowser, url: URL},
+    async function(browser) {
+      ChromeUtils.registerWindowActor("Test", windowActorOptions);
+      let parent = browser.browsingContext.currentWindowGlobal;
+      let actorParent = parent.getActor("Test");
+      ok(actorParent, "JSWindowActorParent should have value.");
+
+      await ContentTask.spawn(
+        browser, {}, async function() {
+          let child = content.window.getWindowGlobalChild();
+          let actorChild = child.getActor("Test");
+          ok(actorChild, "JSWindowActorChild should have value.");
+
+          let promise = new Promise(resolve => {
+            actorChild.sendAsyncMessage("Test", "init", {});
+            actorChild.done = (data) => resolve(data);
+          }).then(data => {
+            ok(data.initial, "Initial should be true.");
+            ok(data.toParent, "ToParent should be true.");
+            ok(data.toChild, "ToChild should be true.");
+          });
+
+          await promise;
+        });
+        ChromeUtils.unregisterWindowActor("Test");
+    });
+});
+
+add_task(async function test_asyncMessage_without_both_side_actor() {
+  await BrowserTestUtils.withNewTab({gBrowser, url: URL},
+    async function(browser) {
+      ChromeUtils.registerWindowActor("Test", windowActorOptions);
+      // If we don't create a parent actor, make sure the parent actor
+      // gets created by having sent the message.
+      await ContentTask.spawn(
+        browser, {}, async function() {
+          let child = content.window.getWindowGlobalChild();
+          let actorChild = child.getActor("Test");
+          ok(actorChild, "JSWindowActorChild should have value.");
+
+          let promise = new Promise(resolve => {
+            actorChild.sendAsyncMessage("Test", "init", {});
+            actorChild.done = (data) => resolve(data);
+          }).then(data => {
+            ok(data.initial, "Initial should be true.");
+            ok(data.toParent, "ToParent should be true.");
+            ok(data.toChild, "ToChild should be true.");
+          });
+
+          await promise;
+        });
+        ChromeUtils.unregisterWindowActor("Test");
+    });
+});
--- a/dom/ipc/tests/test_JSWindowActor.xul
+++ b/dom/ipc/tests/test_JSWindowActor.xul
@@ -32,26 +32,54 @@
       ChromeUtils.registerWindowActor("Test", windowActorOptions),
       "Should throw if register has duplicate name.");
     ChromeUtils.unregisterWindowActor("Test");
   });
 
   add_task(async function getActor() {
     // Test in-process getActor function
     ChromeUtils.registerWindowActor("Test", windowActorOptions);
+    SimpleTest.doesThrow(() =>
+      ChromeUtils.registerWindowActor("Test", windowActorOptions),
+      "Should throw if register has duplicate name.");
+    ChromeUtils.unregisterWindowActor("Test");
+  });
+
+  add_task(async function getActor() {
+    // Test in-process getActor function
+    ChromeUtils.registerWindowActor("Test", windowActorOptions);
     let parent = this.window.docShell.browsingContext.currentWindowGlobal;
-    isnot(parent, null, "WindowGlobalParent should have value.");
+    ok(parent, "WindowGlobalParent should have value.");
     let actorParent = parent.getActor("Test");
     is(actorParent.show(), "TestParent", "actor show should have vaule.");
     is(actorParent.manager, parent, "manager should match WindowGlobalParent.");
 
     let child = this.window.getWindowGlobalChild();
+    ok(child, "WindowGlobalChild should have value.");
     is(child.isInProcess, true, "Actor should be in-process.");
     let actorChild = child.getActor("Test");
 
     is(actorChild.show(), "TestChild", "actor show should have vaule.");
     is(actorChild.manager, child, "manager should match WindowGlobalChild.");
     ok(parent.childActor===child, "Actor should be the same.");
     ok(parent.childActor.getActor("Test")===child.getActor("Test"), "GetActor should be the same.");
     ChromeUtils.unregisterWindowActor("Test");
   });
+
+  add_task(async function asyncMessage() {
+    // Test in-process send/recvAsyncMessage function
+    ChromeUtils.registerWindowActor("Test", windowActorOptions);
+    let child = this.window.getWindowGlobalChild();
+    let actorChild = child.getActor("Test");
+    let promise = new Promise(resolve => {
+      actorChild.sendAsyncMessage("Test", "init", {});
+      actorChild.done = (data) => resolve(data);
+    }).then(data => {
+      ok(data.initial, "Initial should be true.");
+      ok(data.toParent, "ToParent should be true.");
+      ok(data.toChild, "ToChild should be true.");
+    });
+
+    await promise;
+    ChromeUtils.unregisterWindowActor("Test");
+  });
   ]]></script>
 </window>
--- a/toolkit/actors/TestChild.jsm
+++ b/toolkit/actors/TestChild.jsm
@@ -6,12 +6,24 @@
 
 var EXPORTED_SYMBOLS = ["TestChild"];
 
 class TestChild extends JSWindowActorChild {
   constructor() {
      super();
   }
 
+  recvAsyncMessage(aMessage) {
+    switch (aMessage.name) {
+      case "toChild":
+        aMessage.data.toChild = true;
+        this.sendAsyncMessage("Test", "toParent", aMessage.data);
+        break;
+      case "done":
+        this.done(aMessage.data);
+        break;
+    }
+  }
+
   show() {
     return "TestChild";
   }
 }
--- a/toolkit/actors/TestParent.jsm
+++ b/toolkit/actors/TestParent.jsm
@@ -6,12 +6,25 @@
 
 var EXPORTED_SYMBOLS = ["TestParent"];
 
 class TestParent extends JSWindowActorParent {
   constructor() {
     super();
   }
 
+  recvAsyncMessage(aMessage) {
+    switch (aMessage.name) {
+      case "init":
+        aMessage.data.initial =  true;
+        this.sendAsyncMessage("Test", "toChild", aMessage.data);
+        break;
+      case "toParent":
+        aMessage.data.toParent = true;
+        this.sendAsyncMessage("Test", "done", aMessage.data);
+        break;
+    }
+  }
+
   show() {
     return "TestParent";
   }
 }