Bug 1444680: Part 1b: Add helper for calling fetch() in content context. r=mixedpuppy
authorKris Maglione <maglione.k@gmail.com>
Sat, 10 Mar 2018 22:52:16 -0800
changeset 407668 b3689730f6bd8f2af457021e1bd0820c720b9d85
parent 407667 599c42a12fcc8eee5e531e50cff162e815623329
child 407669 590b8679be3da45c7923b7b49c2cbddf67222af2
push id100754
push usermaglione.k@gmail.com
push dateMon, 12 Mar 2018 20:51:20 +0000
treeherdermozilla-inbound@156cad0b9e17 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1444680
milestone60.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 1444680: Part 1b: Add helper for calling fetch() in content context. r=mixedpuppy MozReview-Commit-ID: AiefWoKEh5c
toolkit/components/extensions/ExtensionXPCShellUtils.jsm
toolkit/components/extensions/test/xpcshell/test_ext_i18n_css.js
toolkit/components/extensions/test/xpcshell/test_ext_webRequest_filterResponseData.js
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -13,16 +13,18 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonTestUtils",
                                "resource://testing-common/AddonTestUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Extension",
                                "resource://gre/modules/Extension.jsm");
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "MessageChannel",
+                               "resource://gre/modules/MessageChannel.jsm");
 ChromeUtils.defineModuleGetter(this, "Schemas",
                                "resource://gre/modules/Schemas.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "TestUtils",
                                "resource://testing-common/TestUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "Management", () => {
@@ -50,28 +52,36 @@ let BASE_MANIFEST = Object.freeze({
   "manifest_version": 2,
 
   "name": "name",
   "version": "0",
 });
 
 
 function frameScript() {
+  ChromeUtils.import("resource://gre/modules/MessageChannel.jsm");
   ChromeUtils.import("resource://gre/modules/Services.jsm");
 
   Services.obs.notifyObservers(this, "tab-content-frameloader-created");
 
+  const messageListener = {
+    async receiveMessage({target, messageName, recipient, data, name}) {
+      /* globals content */
+      let resp = await content.fetch(data.url, data.options);
+      return resp.text();
+    },
+  };
+  MessageChannel.addListener(this, "Test:Fetch", messageListener);
+
   // eslint-disable-next-line mozilla/balanced-listeners, no-undef
   addEventListener("MozHeapMinimize", () => {
     Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize");
   }, true, true);
 }
 
-const FRAME_SCRIPT = `data:text/javascript,(${encodeURI(frameScript)}).call(this)`;
-
 let kungFuDeathGrip = new Set();
 function promiseBrowserLoaded(browser, url, redirectUrl) {
   return new Promise(resolve => {
     const listener = {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIWebProgressListener]),
 
       onStateChange(webProgress, request, stateFlags, statusCode) {
         let requestUrl = request.URI ? request.URI.spec : webProgress.DOMWindow.location.href;
@@ -134,29 +144,43 @@ class ContentPage {
     if (this.remote) {
       awaitFrameLoader = promiseEvent(browser, "XULFrameLoaderCreated");
       browser.setAttribute("remote", "true");
     }
 
     chromeDoc.documentElement.appendChild(browser);
 
     await awaitFrameLoader;
-    browser.messageManager.loadFrameScript(FRAME_SCRIPT, true);
+    this.browser = browser;
+
+    this.loadFrameScript(frameScript);
+
+    return browser;
+  }
 
-    this.browser = browser;
-    return browser;
+  sendMessage(msg, data) {
+    return MessageChannel.sendMessage(this.browser.messageManager, msg, data);
+  }
+
+  loadFrameScript(func) {
+    let frameScript = `data:text/javascript,(${encodeURI(func)}).call(this)`;
+    this.browser.messageManager.loadFrameScript(frameScript, true);
   }
 
   async loadURL(url, redirectUrl = undefined) {
     await this.browserReady;
 
     this.browser.loadURI(url);
     return promiseBrowserLoaded(this.browser, url, redirectUrl);
   }
 
+  async fetch(url, options) {
+    return this.sendMessage("Test:Fetch", {url, options});
+  }
+
   async close() {
     await this.browserReady;
 
     let {messageManager} = this.browser;
 
     this.browser = null;
 
     this.windowlessBrowser.close();
@@ -612,16 +636,18 @@ var ExtensionTestUtils = {
 
   profileDir: null,
 
   init(scope) {
     this.currentScope = scope;
 
     this.profileDir = scope.do_get_profile();
 
+    this.fetchScopes = new Map();
+
     // We need to load at least one frame script into every message
     // manager to ensure that the scriptable wrapper for its global gets
     // created before we try to access it externally. If we don't, we
     // fail sanity checks on debug builds the first time we try to
     // create a wrapper, because we should never have a global without a
     // cached wrapper.
     Services.mm.loadFrameScript("data:text/javascript,//", true);
 
@@ -644,16 +670,19 @@ var ExtensionTestUtils = {
     Services.dirsvc.registerProvider(dirProvider);
 
 
     scope.registerCleanupFunction(() => {
       tmpD.remove(true);
       Services.dirsvc.unregisterProvider(dirProvider);
 
       this.currentScope = null;
+
+      return Promise.all(Array.from(this.fetchScopes.values(),
+                                    promise => promise.then(scope => scope.close())));
     });
   },
 
   addonManagerStarted: false,
 
   mockAppInfo() {
     const {updateAppInfo} = ChromeUtils.import("resource://testing-common/AppInfo.jsm", {});
     updateAppInfo({
@@ -691,16 +720,27 @@ var ExtensionTestUtils = {
   get remoteContentScripts() {
     return REMOTE_CONTENT_SCRIPTS;
   },
 
   set remoteContentScripts(val) {
     REMOTE_CONTENT_SCRIPTS = !!val;
   },
 
+  async fetch(origin, url, options) {
+    let fetchScopePromise = this.fetchScopes.get(origin);
+    if (!fetchScopePromise) {
+      fetchScopePromise = this.loadContentPage(origin);
+      this.fetchScopes.set(origin, fetchScopePromise);
+    }
+
+    let fetchScope = await fetchScopePromise;
+    return fetchScope.sendMessage("Test:Fetch", {url, options});
+  },
+
   /**
    * Loads a content page into a hidden docShell.
    *
    * @param {string} url
    *        The URL to load.
    * @param {object} [options = {}]
    * @param {ExtensionWrapper} [options.extension]
    *        If passed, load the URL as an extension page for the given
--- a/toolkit/components/extensions/test/xpcshell/test_ext_i18n_css.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_i18n_css.js
@@ -91,44 +91,33 @@ let extensionData = {
 
 async function test_i18n_css(options = {}) {
   extensionData.useAddonManager = options.useAddonManager;
   let extension = ExtensionTestUtils.loadExtension(extensionData);
 
   await extension.startup();
   let cssURL = await extension.awaitMessage("ready");
 
-  function fetch(url) {
-    return new Promise((resolve, reject) => {
-      let xhr = new XMLHttpRequest();
-      xhr.overrideMimeType("text/plain");
-      xhr.open("GET", url);
-      xhr.onload = () => { resolve(xhr.responseText); };
-      xhr.onerror = reject;
-      xhr.send();
-    });
-  }
+  let contentPage = await ExtensionTestUtils.loadContentPage(`${BASE_URL}/file_sample.html`);
 
-  let css = await fetch(cssURL);
+  let css = await contentPage.fetch(cssURL);
 
   equal(css, "body { max-width: 42px; }", "CSS file localized in mochitest scope");
 
-  let contentPage = await ExtensionTestUtils.loadContentPage(`${BASE_URL}/file_sample.html`);
-
   let maxWidth = await extension.awaitMessage("content-maxWidth");
 
   equal(maxWidth, "42px", "stylesheet correctly applied");
 
-  await contentPage.close();
-
   cssURL = cssURL.replace(/foo.css$/, "locale.css");
 
-  css = await fetch(cssURL);
+  css = await contentPage.fetch(cssURL);
   equal(css, '* { content: "en-US ltr rtl left right" }', "CSS file localized in mochitest scope");
 
+  await contentPage.close();
+
   // We don't currently have a good way to mock this.
   if (false) {
     const DIR = "intl.uidirection";
 
     // We don't wind up actually switching the chrome registry locale, since we
     // don't have a chrome package for Hebrew. So just override it, and force
     // RTL directionality.
     const origReqLocales = Services.locale.getRequestedLocales();
--- a/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_filterResponseData.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_webRequest_filterResponseData.js
@@ -3,34 +3,32 @@
 const HOSTS = new Set([
   "example.com",
   "example.org",
   "example.net",
 ]);
 
 const server = createHttpServer({hosts: HOSTS});
 
+const FETCH_ORIGIN = "http://example.com/dummy";
+
 server.registerPathHandler("/redirect", (request, response) => {
   let params = new URLSearchParams(request.queryString);
   response.setStatusLine(request.httpVersion, 302, "Moved Temporarily");
   response.setHeader("Location", params.get("redirect_uri"));
   response.setHeader("Access-Control-Allow-Origin", "*");
 });
 
 server.registerPathHandler("/dummy", (request, response) => {
   response.setStatusLine(request.httpVersion, 200, "OK");
   response.setHeader("Access-Control-Allow-Origin", "*");
   response.write("ok");
 });
 
-Cu.importGlobalProperties(["fetch"]);
-
 add_task(async function() {
-  const {fetch} = Cu.Sandbox("http://example.com/", {wantGlobalProperties: ["fetch"]});
-
   let extension = ExtensionTestUtils.loadExtension({
     background() {
       let pending = [];
 
       browser.webRequest.onBeforeRequest.addListener(
         data => {
           let filter = browser.webRequest.filterResponseData(data.requestId);
 
@@ -83,18 +81,17 @@ add_task(async function() {
     ["http://example.com/dummy", "http://example.com/dummy"],
     ["http://example.org/dummy", "http://example.org/dummy"],
     ["http://example.net/dummy", "ok"],
     ["http://example.com/redirect?redirect_uri=http://example.com/dummy", "http://example.com/dummy"],
     ["http://example.com/redirect?redirect_uri=http://example.org/dummy", "http://example.org/dummy"],
     ["http://example.com/redirect?redirect_uri=http://example.net/dummy", "ok"],
     ["http://example.net/redirect?redirect_uri=http://example.com/dummy", "http://example.com/dummy"],
   ].map(async ([url, expectedResponse]) => {
-    let resp = await fetch(url);
-    let text = await resp.text();
+    let text = await ExtensionTestUtils.fetch(FETCH_ORIGIN, url);
     equal(text, expectedResponse, `Expected response for ${url}`);
   });
 
   await Promise.all(results);
 
   extension.sendMessage("done");
   await extension.awaitFinish("stream-filter");
   await extension.unload();