diff options
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_redirects.js')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_redirects.js | 660 |
1 files changed, 660 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js b/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js new file mode 100644 index 0000000000..7b950355f3 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js @@ -0,0 +1,660 @@ +"use strict"; + +// Tests whether we can redirect to a moz-extension: url. +ChromeUtils.defineESModuleGetters(this, { + TestUtils: "resource://testing-common/TestUtils.sys.mjs", +}); + +const server = createHttpServer(); +const gServerUrl = `http://localhost:${server.identity.primaryPort}`; + +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.write("redirecting"); +}); + +server.registerPathHandler("/dummy", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("ok"); +}); + +server.registerPathHandler("/dummy-2", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("ok"); +}); + +function onStopListener(channel) { + return new Promise(resolve => { + let orig = channel.QueryInterface(Ci.nsITraceableChannel).setNewListener({ + QueryInterface: ChromeUtils.generateQI([ + "nsIRequestObserver", + "nsIStreamListener", + ]), + getFinalURI(request) { + let { loadInfo } = request; + return (loadInfo && loadInfo.resultPrincipalURI) || request.originalURI; + }, + onDataAvailable(...args) { + orig.onDataAvailable(...args); + }, + onStartRequest(request) { + orig.onStartRequest(request); + }, + onStopRequest(request, statusCode) { + orig.onStopRequest(request, statusCode); + let URI = this.getFinalURI(request.QueryInterface(Ci.nsIChannel)); + resolve(URI && URI.spec); + }, + }); + }); +} + +async function onModifyListener(originUrl, redirectToUrl) { + return TestUtils.topicObserved("http-on-modify-request", (subject, data) => { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + return channel.URI && channel.URI.spec == originUrl; + }).then(([subject, data]) => { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + if (redirectToUrl) { + channel.redirectTo(Services.io.newURI(redirectToUrl)); + } + return channel; + }); +} + +function getExtension( + accessible = false, + background = undefined, + blocking = true +) { + let manifest = { + permissions: ["webRequest", "<all_urls>"], + }; + if (blocking) { + manifest.permissions.push("webRequestBlocking"); + } + if (accessible) { + manifest.web_accessible_resources = ["finished.html"]; + } + if (!background) { + background = () => { + // send the extensions public uri to the test. + let exturi = browser.runtime.getURL("finished.html"); + browser.test.sendMessage("redirectURI", exturi); + }; + } + return ExtensionTestUtils.loadExtension({ + manifest, + files: { + "finished.html": ` + <!DOCTYPE html> + <html> + <head> + <meta charset="utf-8"> + </head> + <body> + <h1>redirected!</h1> + </body> + </html> + `.trim(), + }, + background, + }); +} + +async function redirection_test(url, channelRedirectUrl) { + // setup our observer + let watcher = onModifyListener(url, channelRedirectUrl).then(channel => { + return onStopListener(channel); + }); + let xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.send(); + return watcher; +} + +// This test verifies failure without web_accessible_resources. +add_task(async function test_redirect_to_non_accessible_resource() { + let extension = getExtension(); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let url = `${gServerUrl}/redirect?redirect_uri=${redirectUrl}`; + let result = await redirection_test(url); + equal(result, url, `expected no redirect`); + await extension.unload(); +}); + +// This test makes a request against a server that redirects with a 302. +add_task(async function test_302_redirect_to_extension() { + let extension = getExtension(true); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let url = `${gServerUrl}/redirect?redirect_uri=${redirectUrl}`; + let result = await redirection_test(url); + equal(result, redirectUrl, "redirect request is finished"); + await extension.unload(); +}); + +// This test uses channel.redirectTo during http-on-modify to redirect to the +// moz-extension url. +add_task(async function test_channel_redirect_to_extension() { + let extension = getExtension(true); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let url = `${gServerUrl}/dummy?r=${Math.random()}`; + let result = await redirection_test(url, redirectUrl); + equal(result, redirectUrl, "redirect request is finished"); + await extension.unload(); +}); + +// This test verifies failure without web_accessible_resources. +add_task(async function test_content_redirect_to_non_accessible_resource() { + let extension = getExtension(); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let url = `${gServerUrl}/redirect?redirect_uri=${redirectUrl}`; + let watcher = onModifyListener(url).then(channel => { + return onStopListener(channel); + }); + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl: "about:blank", + }); + equal( + contentPage.browser.documentURI.spec, + "about:blank", + `expected no redirect` + ); + equal(await watcher, url, "expected no redirect"); + await contentPage.close(); + await extension.unload(); +}); + +// This test makes a request against a server that redirects with a 302. +add_task(async function test_content_302_redirect_to_extension() { + let extension = getExtension(true); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let url = `${gServerUrl}/redirect?redirect_uri=${redirectUrl}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`); + await contentPage.close(); + await extension.unload(); +}); + +// This test uses channel.redirectTo during http-on-modify to redirect to the +// moz-extension url. +add_task(async function test_content_channel_redirect_to_extension() { + let extension = getExtension(true); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let url = `${gServerUrl}/dummy?r=${Math.random()}`; + onModifyListener(url, redirectUrl); + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`); + await contentPage.close(); + await extension.unload(); +}); + +// This test makes a request against a server and tests redirect to another server page. +add_task(async function test_extension_302_redirect_web() { + function background(serverUrl) { + let expectedUrls = ["/redirect", "/dummy"]; + let expected = [ + "onBeforeRequest", + "onHeadersReceived", + "onBeforeRedirect", + "onBeforeRequest", + "onHeadersReceived", + "onResponseStarted", + "onCompleted", + ]; + browser.webRequest.onBeforeRequest.addListener( + details => { + browser.test.assertTrue( + details.url.includes(expectedUrls.shift()), + "onBeforeRequest url matches" + ); + browser.test.assertEq( + expected.shift(), + "onBeforeRequest", + "onBeforeRequest matches" + ); + }, + { urls: [serverUrl] } + ); + browser.webRequest.onHeadersReceived.addListener( + details => { + browser.test.assertEq( + expected.shift(), + "onHeadersReceived", + "onHeadersReceived matches" + ); + }, + { urls: [serverUrl] } + ); + browser.webRequest.onResponseStarted.addListener( + details => { + browser.test.assertEq( + expected.shift(), + "onResponseStarted", + "onResponseStarted matches" + ); + }, + { urls: [serverUrl] } + ); + browser.webRequest.onBeforeRedirect.addListener( + details => { + browser.test.assertTrue( + details.redirectUrl.includes("/dummy"), + "onBeforeRedirect matches redirectUrl" + ); + browser.test.assertEq( + expected.shift(), + "onBeforeRedirect", + "onBeforeRedirect matches" + ); + }, + { urls: [serverUrl] } + ); + browser.webRequest.onCompleted.addListener( + details => { + browser.test.assertTrue( + details.url.includes("/dummy"), + "onCompleted expected url received" + ); + browser.test.assertEq( + expected.shift(), + "onCompleted", + "onCompleted matches" + ); + browser.test.notifyPass("requestCompleted"); + }, + { urls: [serverUrl] } + ); + browser.webRequest.onErrorOccurred.addListener( + details => { + browser.test.log(`onErrorOccurred ${JSON.stringify(details)}`); + browser.test.notifyFail("requestCompleted"); + }, + { urls: [serverUrl] } + ); + } + let extension = getExtension( + false, + `(${background})("*://${server.identity.primaryHost}/*")`, + false + ); + await extension.startup(); + let redirectUrl = `${gServerUrl}/dummy`; + let completed = extension.awaitFinish("requestCompleted"); + let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal( + contentPage.browser.documentURI.spec, + redirectUrl, + `expected content redirect` + ); + await completed; + await contentPage.close(); + await extension.unload(); +}); + +// This test makes a request against a server and tests redirect to another server page, without +// onBeforeRedirect. Bug 1448599 +add_task(async function test_extension_302_redirect_opening() { + let redirectUrl = `${gServerUrl}/dummy`; + let expectData = [ + { + event: "onBeforeRequest", + url: `${gServerUrl}/redirect`, + }, + { + event: "onBeforeRequest", + url: redirectUrl, + }, + ]; + function background(serverUrl, expected) { + browser.webRequest.onBeforeRequest.addListener( + details => { + let expect = expected.shift(); + browser.test.assertEq( + expect.event, + "onBeforeRequest", + "onBeforeRequest event matches" + ); + browser.test.assertTrue( + details.url.startsWith(expect.url), + "onBeforeRequest url matches" + ); + if (expected.length === 0) { + browser.test.notifyPass("requestCompleted"); + } + }, + { urls: [serverUrl] } + ); + } + let extension = getExtension( + false, + `(${background})("*://${server.identity.primaryHost}/*", ${JSON.stringify( + expectData + )})`, + false + ); + await extension.startup(); + let completed = extension.awaitFinish("requestCompleted"); + let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal( + contentPage.browser.documentURI.spec, + redirectUrl, + `expected content redirect` + ); + await completed; + await contentPage.close(); + await extension.unload(); +}); + +// This test makes a request against a server and tests redirect to another server page, without +// onBeforeRedirect. Bug 1448599 +add_task(async function test_extension_302_redirect_modify() { + let redirectUrl = `${gServerUrl}/dummy`; + let expectData = [ + { + event: "onHeadersReceived", + url: `${gServerUrl}/redirect`, + }, + { + event: "onHeadersReceived", + url: redirectUrl, + }, + ]; + function background(serverUrl, expected) { + browser.webRequest.onHeadersReceived.addListener( + details => { + let expect = expected.shift(); + browser.test.assertEq( + expect.event, + "onHeadersReceived", + "onHeadersReceived event matches" + ); + browser.test.assertTrue( + details.url.startsWith(expect.url), + "onHeadersReceived url matches" + ); + if (expected.length === 0) { + browser.test.notifyPass("requestCompleted"); + } + }, + { urls: ["<all_urls>"] } + ); + } + let extension = getExtension( + false, + `(${background})("*://${server.identity.primaryHost}/*", ${JSON.stringify( + expectData + )})`, + false + ); + await extension.startup(); + let completed = extension.awaitFinish("requestCompleted"); + let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal( + contentPage.browser.documentURI.spec, + redirectUrl, + `expected content redirect` + ); + await completed; + await contentPage.close(); + await extension.unload(); +}); + +// This test makes a request against a server and tests redirect to another server page, without +// onBeforeRedirect. Bug 1448599 +add_task(async function test_extension_302_redirect_tracing() { + let redirectUrl = `${gServerUrl}/dummy`; + let expectData = [ + { + event: "onCompleted", + url: redirectUrl, + }, + ]; + function background(serverUrl, expected) { + browser.webRequest.onCompleted.addListener( + details => { + let expect = expected.shift(); + browser.test.assertEq( + expect.event, + "onCompleted", + "onCompleted event matches" + ); + browser.test.assertTrue( + details.url.startsWith(expect.url), + "onCompleted url matches" + ); + if (expected.length === 0) { + browser.test.notifyPass("requestCompleted"); + } + }, + { urls: [serverUrl] } + ); + } + let extension = getExtension( + false, + `(${background})("*://${server.identity.primaryHost}/*", ${JSON.stringify( + expectData + )})`, + false + ); + await extension.startup(); + let completed = extension.awaitFinish("requestCompleted"); + let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal( + contentPage.browser.documentURI.spec, + redirectUrl, + `expected content redirect` + ); + await completed; + await contentPage.close(); + await extension.unload(); +}); + +// This test makes a request against a server and tests webrequest. Currently +// disabled due to NS_BINDING_ABORTED happening. +add_task(async function test_extension_302_redirect() { + let extension = getExtension(true, () => { + let myuri = browser.runtime.getURL("*"); + let exturi = browser.runtime.getURL("finished.html"); + browser.webRequest.onBeforeRedirect.addListener( + details => { + browser.test.assertEq(details.redirectUrl, exturi, "redirect matches"); + }, + { urls: ["<all_urls>", myuri] } + ); + browser.webRequest.onCompleted.addListener( + details => { + browser.test.assertEq(details.url, exturi, "expected url received"); + browser.test.notifyPass("requestCompleted"); + }, + { urls: ["<all_urls>", myuri] } + ); + browser.webRequest.onErrorOccurred.addListener( + details => { + browser.test.log(`onErrorOccurred ${JSON.stringify(details)}`); + browser.test.notifyFail("requestCompleted"); + }, + { urls: ["<all_urls>", myuri] } + ); + // send the extensions public uri to the test. + browser.test.sendMessage("redirectURI", exturi); + }); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let completed = extension.awaitFinish("requestCompleted"); + let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal( + contentPage.browser.documentURI.spec, + redirectUrl, + `expected content redirect` + ); + await completed; + await contentPage.close(); + await extension.unload(); +}).skip(); + +// This test makes a request and uses onBeforeRequet to redirect to moz-ext. +// Currently disabled due to NS_BINDING_ABORTED happening. +add_task(async function test_extension_redirect() { + let extension = getExtension(true, () => { + let myuri = browser.runtime.getURL("*"); + let exturi = browser.runtime.getURL("finished.html"); + browser.webRequest.onBeforeRequest.addListener( + details => { + return { redirectUrl: exturi }; + }, + { urls: ["<all_urls>", myuri] }, + ["blocking"] + ); + browser.webRequest.onBeforeRedirect.addListener( + details => { + browser.test.assertEq(details.redirectUrl, exturi, "redirect matches"); + }, + { urls: ["<all_urls>", myuri] } + ); + browser.webRequest.onCompleted.addListener( + details => { + browser.test.assertEq(details.url, exturi, "expected url received"); + browser.test.notifyPass("requestCompleted"); + }, + { urls: ["<all_urls>", myuri] } + ); + browser.webRequest.onErrorOccurred.addListener( + details => { + browser.test.log(`onErrorOccurred ${JSON.stringify(details)}`); + browser.test.notifyFail("requestCompleted"); + }, + { urls: ["<all_urls>", myuri] } + ); + // send the extensions public uri to the test. + browser.test.sendMessage("redirectURI", exturi); + }); + await extension.startup(); + let redirectUrl = await extension.awaitMessage("redirectURI"); + let completed = extension.awaitFinish("requestCompleted"); + let url = `${gServerUrl}/dummy?r=${Math.random()}`; + let contentPage = await ExtensionTestUtils.loadContentPage(url, { + redirectUrl, + }); + equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`); + await completed; + await contentPage.close(); + await extension.unload(); +}).skip(); + +add_task(async function test_redirect_with_onHeadersReceived() { + let redirectUrl = `${gServerUrl}/dummy-2`; + + function background(initialUrl, redirectUrl) { + browser.webRequest.onCompleted.addListener( + () => { + browser.test.notifyPass("requestCompleted"); + }, + { urls: ["<all_urls>"] } + ); + + browser.webRequest.onHeadersReceived.addListener( + () => { + // Redirect to a different URL when we receive the headers of the + // initial request. + return { redirectUrl }; + }, + { urls: [initialUrl] }, + ["blocking"] + ); + } + let extension = getExtension( + false, + `(${background})("*://${server.identity.primaryHost}/dummy", "${redirectUrl}")` + ); + await extension.startup(); + + let contentPage = await ExtensionTestUtils.loadContentPage( + `${gServerUrl}/dummy` + ); + await extension.awaitFinish("requestCompleted"); + equal(contentPage.browser.documentURI.spec, redirectUrl, "expected redirect"); + + await contentPage.close(); + await extension.unload(); +}); + +add_task(async function test_no_redirect_with_location_in_onHeadersReceived() { + function background(initialUrl, redirectUrl) { + browser.webRequest.onCompleted.addListener( + ({ responseHeaders }) => { + // Make sure that the `Location` header is set by `onHeadersReceived`. + browser.test.assertTrue( + responseHeaders.some(({ name, value }) => { + return name.toLowerCase() === "location" && value === redirectUrl; + }), + "Location header is set" + ); + + browser.test.notifyPass("requestCompleted"); + }, + { urls: ["<all_urls>"] }, + ["responseHeaders"] + ); + + browser.webRequest.onHeadersReceived.addListener( + ({ responseHeaders }) => { + return { + responseHeaders: [ + ...responseHeaders, + // Although we set a Location header here, the request shouldn't be + // redirected to `redirectUrl` because the status code hasn't been + // change (and cannot be changed from there). + { name: "Location", value: redirectUrl }, + ], + }; + }, + { urls: [initialUrl] }, + ["blocking", "responseHeaders"] + ); + } + let extension = getExtension( + false, + `(${background})("*://${server.identity.primaryHost}/dummy", "${gServerUrl}/dummy-2")` + ); + await extension.startup(); + + let initialUrl = `${gServerUrl}/dummy`; + let contentPage = await ExtensionTestUtils.loadContentPage(initialUrl); + await extension.awaitFinish("requestCompleted"); + equal( + contentPage.browser.documentURI.spec, + initialUrl, + "expected no redirect" + ); + + await contentPage.close(); + await extension.unload(); +}); |