# HG changeset patch # Parent e4c31266f84c5b640cdccb75bf611cc9f2602a3f Bug 1276048 - webextension tests diff --git a/browser/components/extensions/test/browser/browser-common.ini b/browser/components/extensions/test/browser/browser-common.ini --- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -19,16 +19,17 @@ support-files = file_language_ja.html file_language_tlh.html file_dummy.html file_inspectedwindow_reload_target.sjs file_serviceWorker.html webNav_createdTarget.html webNav_createdTargetSource.html webNav_createdTargetSource_subframe.html + redirect.sjs serviceWorker.js searchSuggestionEngine.xml searchSuggestionEngine.sjs ../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js [browser_ext_browserAction_area.js] [browser_ext_browserAction_context.js] [browser_ext_browserAction_contextMenu.js] @@ -85,16 +86,18 @@ skip-if = debug || asan # Bug 1354681 [browser_ext_pageAction_simple.js] [browser_ext_pageAction_telemetry.js] [browser_ext_pageAction_title.js] [browser_ext_popup_api_injection.js] [browser_ext_popup_background.js] [browser_ext_popup_corners.js] [browser_ext_popup_sendMessage.js] [browser_ext_popup_shutdown.js] +[browser_ext_redirect.js] +[browser_ext_redirect_chrome.js] [browser_ext_runtime_openOptionsPage.js] [browser_ext_runtime_openOptionsPage_uninstall.js] [browser_ext_runtime_setUninstallURL.js] [browser_ext_sessions_forgetClosedTab.js] [browser_ext_sessions_forgetClosedWindow.js] [browser_ext_sessions_getRecentlyClosed.js] [browser_ext_sessions_getRecentlyClosed_private.js] [browser_ext_sessions_getRecentlyClosed_tabs.js] diff --git a/browser/components/extensions/test/browser/browser_ext_redirect.js b/browser/components/extensions/test/browser/browser_ext_redirect.js new file mode 100644 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_redirect.js @@ -0,0 +1,156 @@ +"use strict"; + +const testExtension = { + manifest: { + "permissions": [ + "webRequest", + "webRequestBlocking", + "tabs", + "https://example.com/", + ], + }, + files: { + "finished.html": ` + + + + + + +

redirected!

+ + + `.trim(), + }, +}; + +function backgroundScript(expectFail) { + let exturi = browser.extension.getURL("/finished.html"); + let uri = "https://example.com/browser/browser/components/extensions/test/browser/redirect.sjs"; + let url = `${uri}?redirect_uri=${exturi}`; + + browser.webRequest.onBeforeRequest.addListener(details => { + browser.test.log(`onBeforeRequest url ${details.url}`); + }, {urls: [""]}, ["blocking"]); + browser.webRequest.onBeforeRedirect.addListener(details => { + browser.test.log(`onBeforeRedirect url from [${details.url}] to [${details.redirectUrl}]`); + }, {urls: [""]}); + browser.webRequest.onCompleted.addListener(details => { + browser.test.assertEq(exturi, details.url, "got correct url on redirect"); + }, {urls: [""]}); + browser.webRequest.onErrorOccurred.addListener(details => { + if (!expectFail) { + browser.test.fail(`moz-ext redirect failed ${details.url}`); + return; + } + browser.test.log(`onErrorOccurred url from [${details.url}]`); + }, {urls: [""]}); + + let loadingUrl; + browser.tabs.onUpdated.addListener((changedTabId, changed) => { + browser.test.log(`tabs.onUpdated ${changedTabId} ${JSON.stringify(changed)}`); + if (changed.status === "loading") { + loadingUrl = changed.url; + } + if (changed.status === "complete" && changed.url || loadingUrl) { + if (expectFail) { + browser.test.assertEq(url, changed.url || loadingUrl, "tab should not redirect to moz-extension"); + } else { + browser.test.assertEq(exturi, changed.url || loadingUrl, "tab should redirect to moz-extension"); + } + browser.tabs.remove(changedTabId); + browser.test.sendMessage("done"); + } + }); + browser.tabs.create({url}); +} + +// Tests whether we can redirect to a moz-extension: url. This should result +// in a Corrupted Content Error page with an unchanged url. +add_task(function* test_extRedirect() { + testExtension.background = `(${backgroundScript})(true)`; + let extension = ExtensionTestUtils.loadExtension(testExtension); + + yield extension.startup(); + yield extension.awaitMessage("done"); + yield extension.unload(); +}); + +// Tests whether we can redirect to a moz-extension: url. This test adds the +// redirect destination to web_accessible_resources which should allow the +// redirect to occure. +add_task(function* test_extRedirect_accessible() { + testExtension.background = `(${backgroundScript})(false)`; + testExtension.manifest.web_accessible_resources = ["finished.html"]; + let extension = ExtensionTestUtils.loadExtension(testExtension); + + yield extension.startup(); + yield extension.awaitMessage("done"); + yield extension.unload(); +}); + +// Tests whether we can redirect to a moz-extension: url. +add_task(function* test_extRedirect_webRequest() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "permissions": [ + "tabs", + "webRequest", + "webRequestBlocking", + "", + ], + "web_accessible_resources": ["finished.html"], + }, + files: { + "finished.html": ` + + + + + + +

redirected!

+ + + `.trim(), + }, + background() { + let url = `http://example.com/browser/browser/components/extensions/test/browser/file_dummy.html?r=${Math.random()}`; + let exturi = browser.extension.getURL("finished.html"); + browser.webRequest.onBeforeRequest.addListener(details => { + browser.test.log(`onBeforeRequest url ${details.url}`); + if (details.url.includes("file_dummy.html")) { + browser.test.log(`... redirecting to ${exturi}`); + return {redirectUrl: exturi}; + } + }, {urls: [""]}, ["blocking"]); + browser.webRequest.onBeforeRedirect.addListener(details => { + browser.test.log(`onBeforeRedirect url from [${details.url}] to [${details.redirectUrl}]`); + }, {urls: [""]}); + browser.webRequest.onCompleted.addListener(details => { + browser.test.assertEq(exturi, details.url, "got correct url on redirect"); + }, {urls: [""]}); + browser.webRequest.onErrorOccurred.addListener(details => { + browser.test.fail(`moz-ext redirect failed ${details.url}`); + }, {urls: [""]}); + + let loadingUrl; + browser.tabs.onUpdated.addListener((changedTabId, changed) => { + browser.test.log(`tabs.onUpdated ${changedTabId} ${JSON.stringify(changed)}`); + if (changed.status === "loading") { + loadingUrl = changed.url; + } + if (changed.status === "complete") { + browser.test.assertEq(exturi, changed.url || loadingUrl, "tab should redirect to moz-extension"); + browser.tabs.remove(changedTabId); + browser.test.sendMessage("done"); + } + }); + browser.tabs.create({url}); + }, + }); + + yield extension.startup(); + yield extension.awaitMessage("done"); + yield extension.unload(); +}); diff --git a/browser/components/extensions/test/browser/browser_ext_redirect_chrome.js b/browser/components/extensions/test/browser/browser_ext_redirect_chrome.js new file mode 100644 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_redirect_chrome.js @@ -0,0 +1,116 @@ +"use strict"; + +// Tests whether we can redirect to a moz-extension: url. + +// nsIWebRequestListener is a nsIThreadRetargetableStreamListener that handles +// forwarding of nsIRequestObserver for JS consumers. It does nothing more +// than that. +let WebRequestListener = Components.Constructor("@mozilla.org/webextensions/webRequestListener;1", + "nsIWebRequestListener", "init"); + +function onStopListener(channel) { + info(`setup stream listener`); + return new Promise(resolve => { + new WebRequestListener({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver, + Ci.nsIStreamListener]), + onStartRequest: function(request, context) { + info(`setup stream onStartRequest uri [${request.URI && request.URI.spec}]`); + }, + onStopRequest(request, context, statusCode) { + info(`setup stream onStopRequest uri [${request.URI && request.URI.spec}]`); + resolve(request.URI && request.URI.spec); + }, + }, channel); + channel.resume(); + }); +} + +function onModifyListener(originUrl, redirectToUrl) { + let obs = Services.obs; + return new Promise(resolve => { + obs.addObserver({ + observe: function(subject, topic, data) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + if (channel.URI && channel.URI.spec != originUrl) { + return; + } + obs.removeObserver(this, "http-on-modify-request"); + channel.suspend(); + if (redirectToUrl) { + info(`redirecting channel to ${redirectToUrl}`); + channel.redirectTo(Services.io.newURI(redirectToUrl)); + } + onStopListener(channel).then(finalUrl => { + info(`onStop has completed ${finalUrl}`); + resolve(finalUrl); + }); + }, + }, "http-on-modify-request"); + }); +} + +let extension; +let redirectUrl; +add_task(function* startup() { + // This extension does nothing except provide us a public moz-extension url + // that we can land on. + extension = ExtensionTestUtils.loadExtension({ + manifest: { + "web_accessible_resources": ["finished.html"], + }, + files: { + "finished.html": ` + + + + + + +

redirected!

+ + + `.trim(), + }, + background() { + // send the extensions public uri to the test. + let exturi = browser.extension.getURL("finished.html"); + browser.test.sendMessage("redirectURI", exturi); + }, + }); + yield extension.startup(); + redirectUrl = yield extension.awaitMessage("redirectURI"); +}); + +function* redirection_test(url, redirectTo) { + // setup our observer + let watcher = onModifyListener(url, redirectTo); + // open a tab to the url and wait for watcher. + BrowserTestUtils.openNewForegroundTab(gBrowser, url); + let finalUrl = yield watcher; + if (!finalUrl) { + // non-e10, get the url off the tab + finalUrl = gBrowser.selectedTab.linkedBrowser.currentURI.spec; + } + is(finalUrl, redirectUrl, "redirect request is finished"); + // The tab never finished loading, presumably due to the redirect failure, + // so waiting on the tab here will hang. + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); +} + +// This test makes a request against a server that redirects with a 302. +add_task(function* test_302_redirect_to_extension() { + let url = `https://example.com/browser/browser/components/extensions/test/browser/redirect.sjs?redirect_uri=${redirectUrl}`; + yield redirection_test(url); +}); + +// This test uses channel.redirectTo during http-on-modify to redirect to the +// moz-extension url. +add_task(function* test_channel_redirect_to_extension() { + let url = `http://example.com/browser/browser/components/extensions/test/browser/file_dummy.html?r=${Math.random()}`; + yield redirection_test(url, redirectUrl); +}); + +add_task(function* cleanup() { + yield extension.unload(); +}); diff --git a/browser/components/extensions/test/browser/redirect.sjs b/browser/components/extensions/test/browser/redirect.sjs new file mode 100644 --- /dev/null +++ b/browser/components/extensions/test/browser/redirect.sjs @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +Components.utils.importGlobalProperties(["URLSearchParams"]); + +function handleRequest(request, response) { + let params = new URLSearchParams(request.queryString); + if (params.has("no_redirect")) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("ok"); + } else { + response.setStatusLine(request.httpVersion, 302, "Moved Temporarily"); + dump("***** redirecting to ", params.get("redirect_uri")+"\n"); + response.setHeader("Location", params.get("redirect_uri")); + } +} diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -1353,17 +1353,18 @@ class CacheStore { let db; let result; try { db = await StartupCache.open(); result = await db.objectStore(this.storeName) .get(key); } catch (e) { - Cu.reportError(e); + // ugh. this produces too many tracebacks running tests. + //Cu.reportError(e); return createFunc(key); } if (result === undefined) { let value = await createFunc(key); result = {key, value}; diff --git a/toolkit/modules/addons/WebRequest.jsm b/toolkit/modules/addons/WebRequest.jsm --- a/toolkit/modules/addons/WebRequest.jsm +++ b/toolkit/modules/addons/WebRequest.jsm @@ -826,17 +826,22 @@ HttpObserverManager = { } return Object.assign(data, extraData); }, canModify(channel) { let {isHostPermitted} = AddonManagerPermissions; - if (isHostPermitted(channel.URI.host)) { + try { + // TODO Figure out why we have jar urls on redirect now. + channel.QueryInterface(Ci.nsIJARChannel); + return false; + } catch (e) {} + if (channel.URI && isHostPermitted(channel.URI.host)) { return false; } let {loadInfo} = channel; if (loadInfo && loadInfo.loadingPrincipal) { let {loadingPrincipal} = loadInfo; return loadingPrincipal.URI && !isHostPermitted(loadingPrincipal.URI.host);