Bug 1523631 - Part 1: Add a JSWindowActorProtocol object for protocol-specific state, r=jdai
authorNika Layzell <nika@thelayzells.com>
Fri, 22 Feb 2019 15:16:51 +0000
changeset 518531 2beb789b521f78a7ecf8ae6f4138c7b14393e8aa
parent 518530 e89b6e3d3de81c918b6fb6ed334a642d1f46bef2
child 518532 3575c9a372e76640a3a6fe1e3854b74ac2188d9b
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)
reviewersjdai
bugs1523631
milestone67.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 1523631 - Part 1: Add a JSWindowActorProtocol object for protocol-specific state, r=jdai Differential Revision: https://phabricator.services.mozilla.com/D20012
dom/ipc/JSWindowActorService.cpp
dom/ipc/JSWindowActorService.h
--- a/dom/ipc/JSWindowActorService.cpp
+++ b/dom/ipc/JSWindowActorService.cpp
@@ -12,16 +12,156 @@
 #include "mozJSComponentLoader.h"
 
 namespace mozilla {
 namespace dom {
 namespace {
 StaticRefPtr<JSWindowActorService> gJSWindowActorService;
 }
 
+/**
+ * Helper for calling a named method on a JS Window Actor object with a single
+ * parameter.
+ *
+ * It will do the following:
+ *  1. Enter the actor object's compartment.
+ *  2. Convert the given parameter into a JS parameter with ToJSValue.
+ *  3. Call the named method, passing the single parameter.
+ *  4. Place the return value in aRetVal.
+ *
+ * If an error occurs during this process, this method clears any pending
+ * exceptions, and returns a nsresult.
+ */
+template <typename T>
+nsresult CallJSActorMethod(nsWrapperCache* aActor, const char* aName,
+                           T& aNativeArg, JS::MutableHandleValue aRetVal) {
+  // FIXME(nika): We should avoid atomizing and interning the |aName| strings
+  // every time we do this call. Given the limited set of possible IDs, it would
+  // be better to cache the `jsid` values.
+
+  aRetVal.setUndefined();
+
+  // Get the wrapper for our actor. If we don't have a wrapper, the target
+  // method won't be defined on it. so there's no reason to continue.
+  JS::Rooted<JSObject*> actor(RootingCx(), aActor->GetWrapper());
+  if (NS_WARN_IF(!actor)) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // Enter the realm of our actor object to begin running script.
+  AutoEntryScript aes(actor, "CallJSActorMethod");
+  JSContext* cx = aes.cx();
+  JSAutoRealm ar(cx, actor);
+
+  // Get the method we want to call, and produce NS_ERROR_NOT_IMPLEMENTED if
+  // it is not present.
+  JS::Rooted<JS::Value> func(cx);
+  if (NS_WARN_IF(!JS_GetProperty(cx, actor, aName, &func) ||
+                 func.isPrimitive())) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // Convert the native argument to a JS value.
+  JS::Rooted<JS::Value> argv(cx);
+  if (NS_WARN_IF(!ToJSValue(cx, aNativeArg, &argv))) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_FAILURE;
+  }
+
+  // Call our method.
+  if (NS_WARN_IF(!JS_CallFunctionValue(cx, actor, func,
+                                       JS::HandleValueArray(argv), aRetVal))) {
+    JS_ClearPendingException(cx);
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+/**
+ * Object corresponding to a single 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 JSWindowActorService.
+ */
+class JSWindowActorProtocol final : public nsISupports {
+ public:
+  NS_DECL_ISUPPORTS
+
+  static already_AddRefed<JSWindowActorProtocol> FromIPC(
+      const JSWindowActorInfo& aInfo);
+  JSWindowActorInfo ToIPC();
+
+  static already_AddRefed<JSWindowActorProtocol> FromWebIDLOptions(
+      const nsAString& aName, const WindowActorOptions& aOptions,
+      ErrorResult& aRv);
+
+  struct Sided {
+    nsCString mModuleURI;
+  };
+
+  struct ParentSide : public Sided {};
+
+  struct ChildSide : public Sided {};
+
+  const nsAString& Name() const { return mName; }
+  const ParentSide& Parent() const { return mParent; }
+  const ChildSide& Child() const { return mChild; }
+
+  void RegisterListenersFor(EventTarget* aRoot);
+  void UnregisterListenersFor(EventTarget* aRoot);
+
+ private:
+  explicit JSWindowActorProtocol(const nsAString& aName) : mName(aName) {}
+
+  ~JSWindowActorProtocol() = default;
+
+  nsString mName;
+  ParentSide mParent;
+  ChildSide mChild;
+};
+
+NS_IMPL_ISUPPORTS0(JSWindowActorProtocol);
+
+/* static */ already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
+  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+  RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name());
+  proto->mChild.mModuleURI.Assign(aInfo.url());
+
+  return proto.forget();
+}
+
+JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
+  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+  JSWindowActorInfo info;
+  info.name() = mName;
+  info.url() = mChild.mModuleURI;
+
+  return info;
+}
+
+already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromWebIDLOptions(const nsAString& aName,
+                                         const WindowActorOptions& aOptions,
+                                         ErrorResult& aRv) {
+  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+  RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName);
+  proto->mParent.mModuleURI = aOptions.mParent.mModuleURI;
+  proto->mChild.mModuleURI = aOptions.mChild.mModuleURI;
+
+  return proto.forget();
+}
+
 JSWindowActorService::JSWindowActorService() { MOZ_ASSERT(NS_IsMainThread()); }
 
 JSWindowActorService::~JSWindowActorService() { MOZ_ASSERT(NS_IsMainThread()); }
 
 /* static */ already_AddRefed<JSWindowActorService>
 JSWindowActorService::GetSingleton() {
   MOZ_ASSERT(NS_IsMainThread());
   if (!gJSWindowActorService) {
@@ -40,88 +180,92 @@ void JSWindowActorService::RegisterWindo
   MOZ_ASSERT(XRE_IsParentProcess());
 
   auto entry = mDescriptors.LookupForAdd(aName);
   if (entry) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
-  entry.OrInsert([&] { return new WindowActorOptions(aOptions); });
+  // Insert a new entry for the protocol.
+  RefPtr<JSWindowActorProtocol> proto =
+      JSWindowActorProtocol::FromWebIDLOptions(aName, aOptions, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
 
-  // Send child's WindowActorOptions to any existing content processes,
-  // because parent's WindowActorOptions can never be accessed in content.
+  entry.OrInsert([&] { return proto; });
+
+  // Send information about the newly added entry to every existing content
+  // process.
+  AutoTArray<JSWindowActorInfo, 1> ipcInfos{proto->ToIPC()};
   for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
-    nsTArray<JSWindowActorInfo> infos;
-    infos.AppendElement(
-        JSWindowActorInfo(nsString(aName), aOptions.mChild.mModuleURI));
-    Unused << cp->SendInitJSWindowActorInfos(infos);
+    Unused << cp->SendInitJSWindowActorInfos(ipcInfos);
   }
 }
 
 void JSWindowActorService::UnregisterWindowActor(const nsAString& aName) {
   mDescriptors.Remove(aName);
 }
 
 void JSWindowActorService::LoadJSWindowActorInfos(
     nsTArray<JSWindowActorInfo>& aInfos) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(XRE_IsContentProcess());
 
   for (uint32_t i = 0, len = aInfos.Length(); i < len; i++) {
-    auto entry = mDescriptors.LookupForAdd(aInfos[i].name());
-
-    entry.OrInsert([&] {
-      WindowActorOptions* option = new WindowActorOptions();
-      option->mChild.mModuleURI.Assign(aInfos[i].url());
-      return option;
-    });
+    // Create our JSWindowActorProtocol, register it in mDescriptors.
+    RefPtr<JSWindowActorProtocol> proto =
+        JSWindowActorProtocol::FromIPC(aInfos[i]);
+    mDescriptors.Put(aInfos[i].name(), proto);
   }
 }
 
 void JSWindowActorService::GetJSWindowActorInfos(
     nsTArray<JSWindowActorInfo>& aInfos) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(XRE_IsParentProcess());
 
   for (auto iter = mDescriptors.ConstIter(); !iter.Done(); iter.Next()) {
-    aInfos.AppendElement(JSWindowActorInfo(nsString(iter.Key()),
-                                           iter.Data()->mChild.mModuleURI));
+    aInfos.AppendElement(iter.Data()->ToIPC());
   }
 }
 
 void JSWindowActorService::ConstructActor(const nsAString& aName,
                                           bool aParentSide,
                                           JS::MutableHandleObject aActor,
                                           ErrorResult& aRv) {
   MOZ_ASSERT_IF(aParentSide, XRE_IsParentProcess());
 
   // Constructing an actor requires a running script, so push an AutoEntryScript
   // onto the stack.
   AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSWindowActor construction");
   JSContext* cx = aes.cx();
 
   // Load our descriptor
-  const WindowActorOptions* descriptor = mDescriptors.Get(aName);
-  if (!descriptor) {
-    MOZ_ASSERT(false, "WindowActorOptions must be found in mDescriptors");
+  RefPtr<JSWindowActorProtocol> proto = mDescriptors.Get(aName);
+  if (!proto) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
-  const WindowActorSidedOptions& side =
-      aParentSide ? descriptor->mParent : descriptor->mChild;
+  const JSWindowActorProtocol::Sided* side;
+  if (aParentSide) {
+    side = &proto->Parent();
+  } else {
+    side = &proto->Child();
+  }
 
   // Load the module using mozJSComponentLoader.
   RefPtr<mozJSComponentLoader> loader = mozJSComponentLoader::Get();
   MOZ_ASSERT(loader);
 
   JS::RootedObject global(cx);
   JS::RootedObject exports(cx);
-  aRv = loader->Import(cx, side.mModuleURI, &global, &exports);
+  aRv = loader->Import(cx, side->mModuleURI, &global, &exports);
   if (aRv.Failed()) {
     return;
   }
   MOZ_ASSERT(exports, "null exports!");
 
   // Load the specific property from our module.
   JS::RootedValue ctor(cx);
   nsAutoString ctorName(aName);
--- a/dom/ipc/JSWindowActorService.h
+++ b/dom/ipc/JSWindowActorService.h
@@ -2,17 +2,17 @@
 /* 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_JSWindowActorService_h
 #define mozilla_dom_JSWindowActorService_h
 
-#include "nsClassHashtable.h"
+#include "nsRefPtrHashtable.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 struct WindowActorOptions;
 class JSWindowActorInfo;
 
 class JSWindowActorService final {
@@ -42,15 +42,15 @@ class JSWindowActorService final {
 
   void ReceiveMessage(JS::RootedObject& aObj, const nsString& aMessageName,
                       ipc::StructuredCloneData& aData);
 
  private:
   JSWindowActorService();
   ~JSWindowActorService();
 
-  nsClassHashtable<nsStringHashKey, WindowActorOptions> mDescriptors;
+  nsRefPtrHashtable<nsStringHashKey, JSWindowActorProtocol> mDescriptors;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_JSWindowActorService_h
\ No newline at end of file