Bug 1189901: part 3: talos-powers: add generic ParentExec. r=mconley
authorAvi Halachmi <avihpit@yahoo.com>
Thu, 29 Sep 2016 21:00:22 +0300
changeset 315855 a72a4afe756ea5664d2391bfa6ec0882e91ffc89
parent 315854 f8719709d98a121fbd36242098026aa0031b303f
child 315856 2b747bcd819e4379a7f4036d271a398b9236802e
push id30757
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:02:43 +0000
treeherdermozilla-central@5ffed033557e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1189901
milestone52.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 1189901: part 3: talos-powers: add generic ParentExec. r=mconley
testing/talos/talos/talos-powers/chrome/talos-powers-content.js
testing/talos/talos/talos-powers/components/TalosPowersService.js
testing/talos/talos/talos-powers/content/TalosPowersContent.js
--- a/testing/talos/talos/talos-powers/chrome/talos-powers-content.js
+++ b/testing/talos/talos/talos-powers/chrome/talos-powers-content.js
@@ -1,12 +1,14 @@
 /* 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/. */
 
+// This file is loaded as a framescript
+
 var { interfaces: Ci, utils: Cu } = Components;
 
 /**
  * Content that wants to quit the whole session should
  * fire the TalosQuitApplication custom event. This will
  * attempt to force-quit the browser.
  */
 addEventListener("TalosQuitApplication", event => {
@@ -75,8 +77,47 @@ addEventListener("TalosPowersContentGetS
       detail: msg.data,
     }, content);
 
     content.dispatchEvent(
       new content.CustomEvent("TalosPowersContentGetStartupInfoResult",
                               event));
   });
 });
+
+/* *
+ * Mediator for the generic ParentExec mechanism.
+ * Listens for a query event from the content, forwards it as a query message
+ * to the parent, listens to a parent reply message, and forwards it as a reply
+ * event for the content to capture.
+ * The consumer API for this mechanism is at content/TalosPowersContent.js
+ * and the callees are at ParentExecServices at components/TalosPowersService.js
+ */
+addEventListener("TalosPowers:ParentExec:QueryEvent", function (e) {
+  if (content.location.protocol != "file:" &&
+      content.location.hostname != "localhost" &&
+      content.location.hostname != "127.0.0.1") {
+    throw new Error("TalosPowers:ParentExec may only be used with local content");
+  }
+  let uniqueMessageId = "TalosPowers:ParentExec:"
+                      + content.document.documentURI + Date.now() + Math.random();
+
+  // Listener for the reply from the parent process
+  addMessageListener("TalosPowers:ParentExec:ReplyMsg", function done(reply) {
+    if (reply.data.id != uniqueMessageId)
+      return;
+
+    removeMessageListener("TalosPowers:ParentExec:ReplyMsg", done);
+
+    // reply to content via an event
+    let contentEvent = Cu.cloneInto({
+      bubbles: true,
+      detail: reply.data.result
+    }, content);
+    content.dispatchEvent(new content.CustomEvent(e.detail.listeningTo, contentEvent));
+  });
+
+  // Send the query to the parent process
+  sendAsyncMessage("TalosPowers:ParentExec:QueryMsg", {
+    command: e.detail.command,
+    id: uniqueMessageId
+  });
+}, false, true);  // wantsUntrusted since we're exposing to unprivileged
--- a/testing/talos/talos/talos-powers/components/TalosPowersService.js
+++ b/testing/talos/talos/talos-powers/components/TalosPowersService.js
@@ -36,16 +36,17 @@ TalosPowersService.prototype = {
   },
 
   init() {
     Services.mm.loadFrameScript(FRAME_SCRIPT, true);
     Services.mm.addMessageListener("Talos:ForceQuit", this);
     Services.mm.addMessageListener("TalosContentProfiler:Command", this);
     Services.mm.addMessageListener("TalosPowersContent:ForceCCAndGC", this);
     Services.mm.addMessageListener("TalosPowersContent:GetStartupInfo", this);
+    Services.mm.addMessageListener("TalosPowers:ParentExec:QueryMsg", this);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
   },
 
   uninit() {
     Services.obs.removeObserver(this, "xpcom-shutdown", false);
   },
 
   receiveMessage(message) {
@@ -61,16 +62,21 @@ TalosPowersService.prototype = {
       case "TalosPowersContent:ForceCCAndGC": {
         Cu.forceGC();
         Cu.forceCC();
         Cu.forceShrinkingGC();
         break;
       }
       case "TalosPowersContent:GetStartupInfo": {
         this.receiveGetStartupInfo(message);
+        break;
+      }
+      case "TalosPowers:ParentExec:QueryMsg": {
+        this.RecieveParentExecCommand(message);
+        break;
       }
     }
   },
 
   /**
    * Enable the SPS profiler with some settings and then pause
    * immediately.
    *
@@ -251,11 +257,48 @@ TalosPowersService.prototype = {
                             startupInfo);
       };
       Services.obs.addObserver(obs, "widget-first-paint", false);
     } else {
       mm.sendAsyncMessage("TalosPowersContent:GetStartupInfo:Result",
                           startupInfo);
     }
   },
+
+  // These services are exposed to local unprivileged content.
+  // Each service is a function which accepts an argument, a callback for sending
+  // the reply (possibly async), and the parent window as a utility.
+  // arg/reply semantice are service-specific.
+  // To add a service: add a method at ParentExecServices here, then at the content:
+  // <script src="chrome://talos-powers-content/content/TalosPowersContent.js"></script>
+  // and then e.g. TalosPowersParent.exec("sampleParentService", myArg, myCallback)
+  // Sample service:
+  /*
+    // arg: anything. return: sample reply
+    sampleParentService: function(arg, callback, win) {
+      win.setTimeout(function() {
+        callback("sample reply for: " + arg);
+      }, 500);
+    },
+
+  */
+  ParentExecServices: {
+  },
+
+  RecieveParentExecCommand(msg) {
+    function sendResult(result) {
+      let mm = msg.target.messageManager;
+      mm.sendAsyncMessage("TalosPowers:ParentExec:ReplyMsg", {
+        id: msg.data.id,
+        result: result
+      });
+    }
+
+    let command = msg.data.command;
+    if (!this.ParentExecServices.hasOwnProperty(command.name))
+      throw new Error("TalosPowers:ParentExec: Invalid service '" + command.name + "'");
+
+    this.ParentExecServices[command.name](command.data, sendResult, msg.target.ownerGlobal);
+  },
+
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TalosPowersService]);
--- a/testing/talos/talos/talos-powers/content/TalosPowersContent.js
+++ b/testing/talos/talos/talos-powers/content/TalosPowersContent.js
@@ -1,13 +1,20 @@
 /* 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/. */
 
+// This file should be executed by the [possibly unprivileged] consumer, e.g.:
+// <script src="chrome://talos-powers-content/content/TalosPowersContent.js"></script>
+// and then e.g. TalosPowersParent.exec("sampleParentService", myArg, myCallback)
+// It marely sends a query event and possibly listens to a reply event, and does not
+// depend on any special privileges.
+
 var TalosPowersContent;
+var TalosPowersParent;
 
 (function() {
   TalosPowersContent = {
     /**
      * Synchronously force CC and GC in this process, as well as in the
      * parent process.
      */
     forceCCAndGC() {
@@ -40,9 +47,45 @@ var TalosPowersContent;
                          function onResult(e) {
           removeEventListener("TalosPowersContentGetStartupInfoResult",
                               onResult);
           resolve(e.detail);
         });
       });
     },
   };
+
+  /**
+   * Generic interface to service functions which run at the parent process.
+   */
+  // If including this script proves too much touble, you may embed the following
+  // code verbatim instead, and keep the copy up to date with its source here:
+  TalosPowersParent = {
+    replyId: 1,
+
+    // dispatch an event to the framescript and register the result/callback event
+    exec: function(commandName, arg, callback, opt_custom_window) {
+      let win = opt_custom_window || window;
+      let replyEvent = "TalosPowers:ParentExec:ReplyEvent:" + this.replyId++;
+      if (callback) {
+        win.addEventListener(replyEvent, function rvhandler(e) {
+          win.removeEventListener(replyEvent, rvhandler);
+          callback(e.detail);
+        });
+      }
+      win.dispatchEvent(
+        new win.CustomEvent("TalosPowers:ParentExec:QueryEvent", {
+          bubbles: true,
+          detail: {
+            command: {
+              name: commandName,
+              data: arg,
+            },
+            listeningTo: replyEvent,
+          }
+        })
+      );
+    },
+
+  };
+  // End of possibly embedded code
+
 })();