Bug 1189901: part 3: talos-powers: add generic ParentExec. r=mconley
☠☠ backed out by 9dfac0d146bc ☠ ☠
authorAvi Halachmi <avihpit@yahoo.com>
Tue, 27 Sep 2016 16:43:37 +0300
changeset 315360 610b70634239c1dc46d4f5052666a09ce712601d
parent 315359 866c0244a7c1a80bc82d1163217241ea079ca552
child 315361 926a610af8a723b75e0f4b5991f978f9e0d745ad
push id82138
push userahalachmi@mozilla.com
push dateTue, 27 Sep 2016 13:43:58 +0000
treeherdermozilla-inbound@14660f4f2b23 [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
+
 })();