Bug 1251139 - Support running a function in the parent with loadChromeScript. r=ted draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Wed, 24 Feb 2016 21:01:39 -0800
changeset 334401 52d36c9e64f226bfd1e515496c96dcd1c90761c3
parent 334363 82629b169420104f46999b0f97897a4be962df8b
child 334412 e0c974992fb39a471c92f108f0d20cf6e1e6b87e
push id11539
push usermozilla@noorenberghe.ca
push dateThu, 25 Feb 2016 05:02:33 +0000
reviewersted
bugs1251139
milestone47.0a1
Bug 1251139 - Support running a function in the parent with loadChromeScript. r=ted MozReview-Commit-ID: 3t7g2bjaFmA
testing/mochitest/tests/Harness_sanity/mochitest.ini
testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html
testing/specialpowers/content/SpecialPowersObserverAPI.js
testing/specialpowers/content/specialpowersAPI.js
--- a/testing/mochitest/tests/Harness_sanity/mochitest.ini
+++ b/testing/mochitest/tests/Harness_sanity/mochitest.ini
@@ -27,16 +27,17 @@ skip-if = buildapp != 'mulet'
 support-files =
     file_app.sjs
     file_app.template.webapp
     app.html
 [test_SpecialPowersPushPrefEnv.html]
 [test_SimpletestGetTestFileURL.html]
 [test_SpecialPowersLoadChromeScript.html]
 support-files = SpecialPowersLoadChromeScript.js
+[test_SpecialPowersLoadChromeScript_function.html]
 [test_bug649012.html]
 [test_bug816847.html]
 skip-if = toolkit == 'android' || e10s #No test app installed
 [test_sanity_cleanup.html]
 [test_sanity_cleanup2.html]
 [test_sanityEventUtils.html]
 skip-if = buildapp == 'mulet' || toolkit == 'android' #bug 688052
 [test_sanitySimpletest.html]
--- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test for SpecialPowers extension</title>
+  <title>Test for SpecialPowers.loadChromeScript</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
copy from testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
copy to testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html
--- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html
@@ -1,23 +1,37 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test for SpecialPowers extension</title>
+  <title>Test for SpecialPowers.loadChromeScript</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
-var url = SimpleTest.getTestFileURL("SpecialPowersLoadChromeScript.js");
-var script = SpecialPowers.loadChromeScript(url);
+
+var script = SpecialPowers.loadChromeScript(function loadChromeScriptTest() {
+  // Copied from SpecialPowersLoadChromeScript.js
+
+  // Just receive 'foo' message and forward it back
+  // as 'bar' message
+  addMessageListener("foo", function (message) {
+    sendAsyncMessage("bar", message);
+  });
+
+  addMessageListener("valid-assert", function (message) {
+    assert.ok(true, "valid assertion");
+    assert.equal(1, 1, "another valid assertion");
+    sendAsyncMessage("valid-assert-done");
+  });
+});
 
 var MESSAGE = { bar: true };
 script.addMessageListener("bar", function (message) {
   is(JSON.stringify(message), JSON.stringify(MESSAGE),
      "received back message from the chrome script");
 
   checkAssert();
 });
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -419,20 +419,30 @@ SpecialPowersObserverAPI.prototype = {
             break;
           default:
             throw new SpecialPowersError("Invalid operation for SPObserverervice");
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPLoadChromeScript": {
-        let url = aMessage.json.url;
         let id = aMessage.json.id;
+        let jsScript;
+        let scriptName;
 
-        let jsScript = this._readUrlAsString(url);
+        if (aMessage.json.url) {
+          jsScript = this._readUrlAsString(aMessage.json.url);
+          scriptName = aMessage.json.url;
+        } else if (aMessage.json.function) {
+          jsScript = aMessage.json.function.body;
+          scriptName = aMessage.json.function.name
+            || "<loadChromeScript anonymous function>";
+        } else {
+          throw new SpecialPowersError("SPLoadChromeScript: Invalid script");
+        }
 
         // Setup a chrome sandbox that has access to sendAsyncMessage
         // and addMessageListener in order to communicate with
         // the mochitest.
         let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
         let sb = Components.utils.Sandbox(systemPrincipal);
         let mm = aMessage.target
                          .QueryInterface(Ci.nsIFrameLoaderOwner)
@@ -446,38 +456,38 @@ SpecialPowersObserverAPI.prototype = {
           this._chromeScriptListeners.push({ id: id, name: name, listener: listener });
         };
         sb.browserElement = aMessage.target;
 
         // Also expose assertion functions
         let reporter = function (err, message, stack) {
           // Pipe assertions back to parent process
           mm.sendAsyncMessage("SPChromeScriptAssert",
-                              { id: id, url: url, err: err, message: message,
-                                stack: stack });
+                              { id, name: scriptName, err, message,
+                                stack });
         };
         Object.defineProperty(sb, "assert", {
           get: function () {
             let scope = Components.utils.createObjectIn(sb);
             Services.scriptloader.loadSubScript("chrome://specialpowers/content/Assert.jsm",
                                                 scope);
 
             let assert = new scope.Assert(reporter);
             delete sb.assert;
             return sb.assert = assert;
           },
           configurable: true
         });
 
         // Evaluate the chrome script
         try {
-          Components.utils.evalInSandbox(jsScript, sb, "1.8", url, 1);
+          Components.utils.evalInSandbox(jsScript, sb, "1.8", scriptName, 1);
         } catch(e) {
           throw new SpecialPowersError(
-            "Error while executing chrome script '" + url + "':\n" +
+            "Error while executing chrome script '" + scriptName + "':\n" +
             e + "\n" +
             e.fileName + ":" + e.lineNumber);
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
       case "SPChromeScriptMessage": {
         let id = aMessage.json.id;
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -448,25 +448,34 @@ SpecialPowersAPI.prototype = {
   get MockColorPicker() {
     return MockColorPicker;
   },
 
   get MockPermissionPrompt() {
     return MockPermissionPrompt;
   },
 
-  loadChromeScript: function (url) {
+  loadChromeScript: function (urlOrFunction) {
     // Create a unique id for this chrome script
     let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
                           .getService(Ci.nsIUUIDGenerator);
     let id = uuidGenerator.generateUUID().toString();
 
     // Tells chrome code to evaluate this chrome script
+    let scriptArgs = { id };
+    if (typeof(urlOrFunction) == "function") {
+      scriptArgs.function = {
+        body: "(" + urlOrFunction.toString() + ")();",
+        name: urlOrFunction.name,
+      };
+    } else {
+      scriptArgs.url = urlOrFunction;
+    }
     this._sendSyncMessage("SPLoadChromeScript",
-                          { url: url, id: id });
+                          scriptArgs);
 
     // Returns a MessageManager like API in order to be
     // able to communicate with this chrome script
     let listeners = [];
     let chromeScript = {
       addMessageListener: (name, listener) => {
         listeners.push({ name: name, listener: listener });
       },
@@ -504,17 +513,17 @@ SpecialPowersAPI.prototype = {
         }
       }
     };
     this._addMessageListener("SPChromeScriptMessage", chromeScript);
     this._addMessageListener("SPChromeScriptAssert", chromeScript);
 
     let assert = json => {
       // An assertion has been done in a mochitest chrome script
-      let {url, err, message, stack} = json;
+      let {name, err, message, stack} = json;
 
       // Try to fetch a test runner from the mochitest
       // in order to properly log these assertions and notify
       // all usefull log observers
       let window = this.window.get();
       let parentRunner, repr = o => o;
       if (window) {
         window = window.wrappedJSObject;
@@ -530,20 +539,20 @@ SpecialPowersAPI.prototype = {
         message ? message :
                   ("assertion @ " + stack.filename + ":" + stack.lineNumber);
       if (err) {
         diagnostic +=
           " - got " + repr(err.actual) +
           ", expected " + repr(err.expected) +
           " (operator " + err.operator + ")";
       }
-      var msg = [resultString, url, diagnostic].join(" | ");
+      var msg = [resultString, name, diagnostic].join(" | ");
       if (parentRunner) {
         if (err) {
-          parentRunner.addFailedTest(url);
+          parentRunner.addFailedTest(name);
           parentRunner.error(msg);
         } else {
           parentRunner.log(msg);
         }
       } else {
         // When we are running only a single mochitest, there is no test runner
         dump(msg + "\n");
       }