Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>
Test StreamFilter-monitored responses for ServiceWorker-intercepted requests
</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
// eslint-disable-next-line mozilla/no-addtask-setup
add_task(async function setup() {
SimpleTest.waitForExplicitFinish();
await SpecialPowers.pushPrefEnv({
set: [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
],
});
const registration = await navigator.serviceWorker.register(
"streamfilter_worker.js"
);
SimpleTest.registerCleanupFunction(async function unregisterRegistration() {
await registration.unregister();
});
await new Promise(resolve => {
const serviceWorker = registration.installing;
serviceWorker.onstatechange = () => {
if (serviceWorker.state == "activated") {
resolve();
}
};
});
ok(navigator.serviceWorker.controller, "Page is controlled");
});
async function getExtension() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["webRequest", "webRequestBlocking", "<all_urls>"],
},
// This WebExtension only proxies a response's data through a StreamFilter;
// it doesn't modify the data itself in any way.
background() {
class FilterWrapper {
constructor(requestId) {
const filter = browser.webRequest.filterResponseData(requestId);
const arrayBuffers = [];
filter.onstart = () => {
browser.test.sendMessage("start");
};
filter.ondata = ({ data }) => {
arrayBuffers.push(data);
};
filter.onstop = () => {
browser.test.sendMessage("stop");
new Blob(arrayBuffers).arrayBuffer().then(buffer => {
filter.write(buffer);
filter.close();
});
};
filter.onerror = () => {
// We only ever expect a redirect error here.
browser.test.assertEq(filter.error, "ServiceWorker fallback redirection");
browser.test.sendMessage("error");
};
}
}
browser.webRequest.onBeforeRequest.addListener(
details => {
new FilterWrapper(details.requestId);
},
{
urls: ["<all_urls>"],
types: ["xmlhttprequest"],
},
["blocking"]
);
},
});
await extension.startup();
return extension;
}
const streamFilterServerUrl = `${location.origin}/tests/dom/serviceworkers/test/streamfilter_server.sjs`;
const requestUrlForServerQueryString = "syntheticResponse=0";
// streamfilter_server.sjs is expected to respond to a request to this URL.
const requestUrlForServer = `${streamFilterServerUrl}?${requestUrlForServerQueryString}`;
const requestUrlForServiceWorkerQueryString = "syntheticResponse=1";
// streamfilter_worker.js is expected to respond to a request to this URL.
const requestUrlForServiceWorker = `${streamFilterServerUrl}?${requestUrlForServiceWorkerQueryString}`;
// startNetworkerRequestFn must be a function that, when called, starts a
// network request and returns a promise that resolves after the request
// completes (or fails). This function will return the value that that promise
// resolves with (or throw if it rejects).
async function observeFilteredNetworkRequest(startNetworkRequestFn, promises) {
const networkRequestPromise = startNetworkRequestFn();
await Promise.all(promises);
return networkRequestPromise;
}
// Returns a promise that resolves with the XHR's response text.
function callXHR(requestUrl, promises) {
return observeFilteredNetworkRequest(() => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = () => {
resolve(xhr.responseText);
};
xhr.onerror = reject;
xhr.open("GET", requestUrl);
xhr.send();
});
}, promises);
}
// Returns a promise that resolves with the Fetch's response text.
function callFetch(requestUrl, promises) {
return observeFilteredNetworkRequest(() => {
return fetch(requestUrl).then(response => response.text());
}, promises);
}
// The expected response text is always the query string (without the leading
// "?") of the request URL.
add_task(async function callXhrExpectServerResponse() {
info(`Performing XHR at ${requestUrlForServer}...`);
let extension = await getExtension();
is(
await callXHR(requestUrlForServer, [
extension.awaitMessage("start"),
extension.awaitMessage("error"),
extension.awaitMessage("stop"),
]),
requestUrlForServerQueryString,
"Server-supplied response for XHR completed successfully"
);
await extension.unload();
});
add_task(async function callXhrExpectServiceWorkerResponse() {
info(`Performing XHR at ${requestUrlForServiceWorker}...`);
let extension = await getExtension();
is(
await callXHR(requestUrlForServiceWorker, [
extension.awaitMessage("start"),
extension.awaitMessage("stop"),
]),
requestUrlForServiceWorkerQueryString,
"ServiceWorker-supplied response for XHR completed successfully"
);
await extension.unload();
});
add_task(async function callFetchExpectServerResponse() {
info(`Performing Fetch at ${requestUrlForServer}...`);
let extension = await getExtension();
is(
await callFetch(requestUrlForServer, [
extension.awaitMessage("start"),
extension.awaitMessage("error"),
extension.awaitMessage("stop"),
]),
requestUrlForServerQueryString,
"Server-supplied response for Fetch completed successfully"
);
await extension.unload();
});
add_task(async function callFetchExpectServiceWorkerResponse() {
info(`Performing Fetch at ${requestUrlForServiceWorker}...`);
let extension = await getExtension();
is(
await callFetch(requestUrlForServiceWorker, [
extension.awaitMessage("start"),
extension.awaitMessage("stop"),
]),
requestUrlForServiceWorkerQueryString,
"ServiceWorker-supplied response for Fetch completed successfully"
);
await extension.unload();
});
</script>
</body>
</html>