Bug 1444680: Part 1b: Add helper for calling fetch() in content context. r?mixedpuppy
MozReview-Commit-ID: AiefWoKEh5c
--- 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_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
@@ -1,13 +1,12 @@
"use strict";
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm");
-ChromeUtils.import("resource://gre/modules/MessageChannel.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
--- 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,34 @@
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 +83,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();