Bug 1273138 fix WebRequest for background pages, r=kmag
MozReview-Commit-ID: DEW9anMmKi2
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -68,24 +68,18 @@ extensions.on("page-shutdown", (type, co
let tab = gBrowser.getTabForBrowser(context.xulBrowser);
if (tab) {
gBrowser.removeTab(tab);
}
}
}
});
-extensions.on("fill-browser-data", (type, browser, data, result) => {
- let tabId = TabManager.getBrowserId(browser);
- if (tabId == -1) {
- result.cancel = true;
- return;
- }
-
- data.tabId = tabId;
+extensions.on("fill-browser-data", (type, browser, data) => {
+ data.tabId = browser ? TabManager.getBrowserId(browser) : -1;
});
/* eslint-enable mozilla/balanced-listeners */
global.currentWindow = function(context) {
let {xulWindow} = context;
if (xulWindow && context.viewType != "background") {
return xulWindow;
}
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/ext-webNavigation.js
@@ -104,36 +104,30 @@ function WebNavigationEventManager(conte
let filters = urlFilters ?
new MatchURLFilters(urlFilters.url) : null;
let listener = data => {
if (!data.browser) {
return;
}
- let tabId = TabManager.getBrowserId(data.browser);
- if (tabId == -1) {
- return;
- }
-
let data2 = {
url: data.url,
timeStamp: Date.now(),
frameId: ExtensionManagement.getFrameId(data.windowId),
parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId),
};
if (eventName == "onErrorOccurred") {
data2.error = data.error;
}
// Fills in tabId typically.
- let result = {};
- extensions.emit("fill-browser-data", data.browser, data2, result);
- if (result.cancel) {
+ extensions.emit("fill-browser-data", data.browser, data2);
+ if (data2.tabId < 0) {
return;
}
fillTransitionProperties(eventName, data, data2);
runSafe(context, callback, data2);
};
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -18,22 +18,19 @@ var {
// EventManager-like class specifically for WebRequest. Inherits from
// SingletonEventManager. Takes care of converting |details| parameter
// when invoking listeners.
function WebRequestEventManager(context, eventName) {
let name = `webRequest.${eventName}`;
let register = (callback, filter, info) => {
let listener = data => {
- if (!data.browser) {
- return;
- }
-
- let tabId = TabManager.getBrowserId(data.browser);
- if (tabId == -1) {
+ // Prevent listening in on requests originating from system principal to
+ // prevent tinkering with OCSP, app and addon updates, etc.
+ if (data.isSystemPrincipal) {
return;
}
let data2 = {
requestId: data.requestId,
url: data.url,
originUrl: data.originUrl,
method: data.method,
@@ -47,22 +44,17 @@ function WebRequestEventManager(context,
if (maybeCached.includes(eventName)) {
data2.fromCache = !!data.fromCache;
}
if ("ip" in data) {
data2.ip = data.ip;
}
- // Fills in tabId typically.
- let result = {};
- extensions.emit("fill-browser-data", data.browser, data2, result);
- if (result.cancel) {
- return;
- }
+ extensions.emit("fill-browser-data", data.browser, data2);
let optional = ["requestHeaders", "responseHeaders", "statusCode", "statusLine", "error", "redirectUrl",
"requestBody"];
for (let opt of optional) {
if (opt in data) {
data2[opt] = data[opt];
}
}
--- a/toolkit/components/extensions/test/mochitest/.eslintrc.js
+++ b/toolkit/components/extensions/test/mochitest/.eslintrc.js
@@ -3,18 +3,21 @@
module.exports = { // eslint-disable-line no-undef
"extends": "../../../../../testing/mochitest/mochitest.eslintrc.js",
"env": {
"webextensions": true,
},
"globals": {
+ "ChromeWorker": false,
+ "onmessage": true,
"sendAsyncMessage": false,
"waitForLoad": true,
"promiseConsoleOutput": true,
"ExtensionTestUtils": false,
"NetUtil": true,
+ "webrequest_test": false,
"XPCOMUtils": true,
},
};
--- a/toolkit/components/extensions/test/mochitest/chrome.ini
+++ b/toolkit/components/extensions/test/mochitest/chrome.ini
@@ -1,13 +1,15 @@
[DEFAULT]
support-files =
chrome_head.js
head.js
file_sample.html
+ webrequest_chromeworker.js
+ webrequest_test.jsm
tags = webextensions
[test_chrome_ext_background_debug_global.html]
skip-if = (os == 'android') # android doesn't have devtools
[test_chrome_ext_background_page.html]
skip-if = (toolkit == 'android') # android doesn't have devtools
[test_chrome_ext_eventpage_warning.html]
[test_chrome_ext_contentscript_unrecognizedprop_warning.html]
@@ -21,8 +23,10 @@ skip-if = (os == 'android') # browser.ta
skip-if = os != "mac" && os != "linux"
[test_ext_cookies_expiry.html]
[test_ext_cookies_permissions.html]
[test_ext_jsversion.html]
[test_ext_schema.html]
[test_chrome_ext_storage_cleanup.html]
[test_chrome_ext_idle.html]
[test_chrome_ext_downloads_saveAs.html]
+[test_chrome_ext_webrequest_background_events.html]
+skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -29,16 +29,17 @@ support-files =
file_script_redirect.js
file_script_xhr.js
file_sample.html
redirection.sjs
file_privilege_escalation.html
file_ext_test_api_injection.js
file_permission_xhr.html
file_teardown_test.js
+ webrequest_worker.js
tags = webextensions
[test_clipboard.html]
# skip-if = # disabled test case with_permission_allow_copy, see inline comment.
[test_ext_inIncognitoContext_window.html]
skip-if = os == 'android' # Android does not currently support windows.
[test_ext_geturl.html]
[test_ext_background_canvas.html]
@@ -86,16 +87,18 @@ skip-if = (os == 'android' || buildapp =
skip-if = (os == 'android') # Android does not support tabs API. Bug 1260250
[test_ext_unload_frame.html]
[test_ext_i18n.html]
skip-if = (os == 'android') # Bug 1258975 on android.
[test_ext_web_accessible_resources.html]
skip-if = (os == 'android') # Bug 1258975 on android.
[test_ext_webrequest.html]
skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
+[test_ext_webrequest_background_events.html]
+skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
[test_ext_webrequest_upload.html]
skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
[test_ext_webnavigation.html]
skip-if = os == 'android' # port.sender.tab is undefined on Android (bug 1258975).
[test_ext_webnavigation_filters.html]
skip-if = os == 'android' # port.sender.tab is undefined on Android (bug 1258975).
[test_ext_subframes_privileges.html]
skip-if = os == 'android' # port.sender.tab is undefined on Android (bug 1258975).
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_webrequest_background_events.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for simple WebExtension</title>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <script type="text/javascript" src="chrome_head.js"></script>
+ <script type="text/javascript" src="head.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+Cu.import(SimpleTest.getTestFileURL("webrequest_test.jsm"));
+let {testFetch, testXHR} = webrequest_test;
+
+// Here we test that any requests originating from a system principal are not
+// accessible through WebRequest. text_ext_webrequest_background_events tests
+// non-system principal requests.
+
+let testExtension = {
+ manifest: {
+ permissions: [
+ "webRequest",
+ "<all_urls>",
+ ],
+ },
+ background() {
+ let eventNames = [
+ "onBeforeRequest",
+ "onBeforeSendHeaders",
+ "onSendHeaders",
+ "onHeadersReceived",
+ "onResponseStarted",
+ "onCompleted",
+ ];
+
+ function listener(name, details) {
+ // If we get anything, we failed. Removing the system principal check
+ // in ext-webrequest triggers this failure.
+ browser.test.fail(`recieved ${name}`);
+ }
+
+ for (let name of eventNames) {
+ browser.webRequest[name].addListener(
+ listener.bind(null, name),
+ {urls: ["https://example.com/*"]}
+ );
+ }
+ },
+};
+
+add_task(function* test_webRequest_chromeworker_events() {
+ let extension = ExtensionTestUtils.loadExtension(testExtension);
+ yield extension.startup();
+ yield new Promise(resolve => {
+ let worker = new ChromeWorker("webrequest_chromeworker.js");
+ worker.onmessage = event => {
+ ok("chrome worker fetch finished");
+ resolve();
+ };
+ worker.postMessage("go");
+ });
+ yield extension.unload();
+});
+
+add_task(function* test_webRequest_chromepage_events() {
+ let extension = ExtensionTestUtils.loadExtension(testExtension);
+ yield extension.startup();
+ yield new Promise(resolve => {
+ fetch("https://example.com/example.txt").then(() => {
+ ok("test page loaded");
+ resolve();
+ });
+ });
+ yield extension.unload();
+});
+
+add_task(function* test_webRequest_jsm_events() {
+ let extension = ExtensionTestUtils.loadExtension(testExtension);
+ yield extension.startup();
+ yield testFetch("https://example.com/example.txt").then(() => {
+ ok("fetch page loaded");
+ });
+ yield testXHR("https://example.com/example.txt").then(() => {
+ ok("xhr page loaded");
+ });
+ yield extension.unload();
+});
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_background_events.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for simple WebExtension</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <script type="text/javascript" src="head.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+add_task(function* test_webRequest_serviceworker_events() {
+ yield SpecialPowers.pushPrefEnv({
+ set: [["dom.serviceWorkers.testing.enabled", true]],
+ });
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: [
+ "webRequest",
+ "<all_urls>",
+ ],
+ },
+ background() {
+ let eventNames = new Set([
+ "onBeforeRequest",
+ "onBeforeSendHeaders",
+ "onSendHeaders",
+ "onHeadersReceived",
+ "onResponseStarted",
+ "onCompleted",
+ ]);
+
+ function listener(name, details) {
+ browser.test.assertTrue(eventNames.has(name), `recieved ${name}`);
+ eventNames.delete(name);
+ if (eventNames.size == 0) {
+ browser.test.sendMessage("done");
+ }
+ }
+
+ for (let name of eventNames) {
+ browser.webRequest[name].addListener(
+ listener.bind(null, name),
+ {urls: ["https://example.com/*"]}
+ );
+ }
+ },
+ });
+
+ yield extension.startup();
+ let registration = yield navigator.serviceWorker.register("webrequest_worker.js", {scope: "."});
+ yield extension.awaitMessage("done");
+ yield registration.unregister();
+ yield extension.unload();
+});
+
+add_task(function* test_webRequest_background_events() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: [
+ "webRequest",
+ "<all_urls>",
+ ],
+ },
+ background() {
+ let eventNames = new Set([
+ "onBeforeRequest",
+ "onBeforeSendHeaders",
+ "onSendHeaders",
+ "onHeadersReceived",
+ "onResponseStarted",
+ "onCompleted",
+ ]);
+
+ function listener(name, details) {
+ browser.test.assertTrue(eventNames.has(name), `recieved ${name}`);
+ eventNames.delete(name);
+ }
+
+ for (let name of eventNames) {
+ browser.webRequest[name].addListener(
+ listener.bind(null, name),
+ {urls: ["https://example.com/*"]}
+ );
+ }
+
+ fetch("https://example.com/example.txt").then(() => {
+ browser.test.assertEq(0, eventNames.size, "messages recieved");
+ browser.test.sendMessage("done");
+ }, () => {
+ browser.test.fail("fetch recieved");
+ browser.test.sendMessage("done");
+ });
+ },
+ });
+
+ yield extension.startup();
+ yield extension.awaitMessage("done");
+ yield extension.unload();
+});
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/webrequest_chromeworker.js
@@ -0,0 +1,8 @@
+"use strict";
+
+onmessage = function(event) {
+ fetch("https://example.com/example.txt").then(() => {
+ postMessage("Done!");
+ });
+};
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/webrequest_test.jsm
@@ -0,0 +1,22 @@
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["webrequest_test"];
+
+Components.utils.importGlobalProperties(["fetch", "XMLHttpRequest"]);
+
+this.webrequest_test = {
+ testFetch(url) {
+ return fetch(url);
+ },
+
+ testXHR(url) {
+ return new Promise(resolve => {
+ let xhr = new XMLHttpRequest();
+ xhr.open("HEAD", url);
+ xhr.onload = () => {
+ resolve();
+ };
+ xhr.send();
+ });
+ },
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/webrequest_worker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+fetch("https://example.com/example.txt");
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -540,24 +540,28 @@ HttpObserverManager = {
method: channel.requestMethod,
browser: browser,
type: WebRequestCommon.typeForPolicyType(policyType),
fromCache: getData(channel).fromCache,
};
if (loadInfo) {
let originPrincipal = loadInfo.triggeringPrincipal || loadInfo.loadingPrincipal;
- if (originPrincipal && originPrincipal.URI) {
+ if (originPrincipal.URI) {
commonData.originUrl = originPrincipal.URI.spec;
}
Object.assign(commonData, {
windowId: loadInfo.frameOuterWindowID ?
loadInfo.frameOuterWindowID : loadInfo.outerWindowID,
parentWindowId: loadInfo.frameOuterWindowID ?
loadInfo.outerWindowID : loadInfo.parentOuterWindowID,
+ isSystemPrincipal: Services.scriptSecurityManager
+ .isSystemPrincipal(loadInfo.triggeringPrincipal) ||
+ Services.scriptSecurityManager
+ .isSystemPrincipal(loadInfo.loadingPrincipal),
});
} else {
Object.assign(commonData, {
windowId: 0,
parentWindowId: 0,
});
}