Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head_webrequest.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script>
"use strict";
let extensionData = {
manifest: {
permissions: ["webRequest", "webRequestBlocking", "<all_urls>", "tabs"],
},
background() {
browser.webRequest.onBeforeRequest.addListener(details => {
if (details.url.endsWith("/favicon.ico")) {
// We don't care about favicon.ico in this test. It is hard to control
// whether the request happens.
browser.test.log(`Ignoring favicon request: ${details.url}`);
return;
}
browser.test.sendMessage("onBeforeRequest", details);
}, {urls: ["<all_urls>"]}, ["blocking"]);
let tab;
browser.tabs.onCreated.addListener(newTab => {
browser.test.sendMessage("tab-created");
tab = newTab;
});
browser.test.onMessage.addListener(msg => {
if (msg === "close-tab") {
browser.tabs.remove(tab.id);
browser.test.sendMessage("tab-closed");
}
});
},
};
let expected = {
"file_simple_xhr.html": {
type: "main_frame",
toplevel: true,
},
"file_image_good.png": {
type: "image",
toplevel: true,
origin: "file_simple_xhr.html",
},
"example.txt": {
type: "xmlhttprequest",
toplevel: true,
origin: "file_simple_xhr.html",
},
// sub frames will have the origin and first ancestor is the
// parent document
"file_simple_xhr_frame.html": {
type: "sub_frame",
toplevelParent: true,
origin: "file_simple_xhr.html",
parent: "file_simple_xhr.html",
},
// a resource in a sub frame will have origin of the subframe,
// but the ancestor chain starts with the parent document
"xhr_resource": {
type: "xmlhttprequest",
origin: "file_simple_xhr_frame.html",
parent: "file_simple_xhr.html",
},
"file_image_bad.png": {
type: "image",
depth: 2,
origin: "file_simple_xhr_frame.html",
parent: "file_simple_xhr.html",
},
"file_simple_xhr_frame2.html": {
type: "sub_frame",
depth: 2,
origin: "file_simple_xhr_frame.html",
parent: "file_simple_xhr_frame.html",
},
"file_image_redirect.png": {
type: "image",
depth: 2,
origin: "file_simple_xhr_frame2.html",
parent: "file_simple_xhr_frame.html",
},
"xhr_resource_2": {
type: "xmlhttprequest",
depth: 2,
origin: "file_simple_xhr_frame2.html",
parent: "file_simple_xhr_frame.html",
},
// This is loaded in a sandbox iframe. originUrl is not available for that,
// and requests within a sandboxed iframe will additionally have an empty
// url on their immediate parent/ancestor.
"file_simple_sandboxed_frame.html": {
type: "sub_frame",
depth: 3,
parent: "file_simple_xhr_frame2.html",
},
"xhr_sandboxed": {
type: "xmlhttprequest",
sandboxed: true,
depth: 3,
parent: "",
},
"file_image_great.png": {
type: "image",
sandboxed: true,
depth: 3,
parent: "",
},
"file_simple_sandboxed_subframe.html": {
type: "sub_frame",
depth: 4,
parent: "",
},
};
function checkDetails(details) {
let url = new URL(details.url);
let filename = url.pathname.split("/").pop();
ok(filename in expected, `Should be expecting a request for ${filename}`);
let expect = expected[filename];
is(expect.type, details.type, `${details.type} type matches`);
if (details.parentFrameId == -1) {
is(details.frameAncestors.length, 0, "no ancestors for main_frame requests");
} else if (details.parentFrameId == 0) {
is(details.frameAncestors.length, 1, "one ancestors for sub_frame requests");
} else {
ok(details.frameAncestors.length > 1, "have multiple ancestors for deep subframe requests");
is(details.frameAncestors.length, expect.depth, "have multiple ancestors for deep subframe requests");
}
if (details.parentFrameId > -1) {
ok(!expect.origin || details.originUrl.includes(expect.origin), "origin url is correct");
is(details.frameAncestors[0].frameId, details.parentFrameId, "first ancestor matches request.parentFrameId");
ok(details.frameAncestors[0].url.includes(expect.parent), "ancestor parent page correct");
is(details.frameAncestors[details.frameAncestors.length - 1].frameId, 0, "last ancestor is always zero");
// All our tests should be somewhere within the frame that we set topframe in the query string. That
// frame will always be the last ancestor.
ok(details.frameAncestors[details.frameAncestors.length - 1].url.includes("topframe=true"), "last ancestor is always topframe");
}
if (expect.toplevel) {
is(details.frameId, 0, "expect load at top level");
is(details.parentFrameId, -1, "expect top level frame to have no parent");
} else if (details.type == "sub_frame") {
ok(details.frameId > 0, "expect sub_frame to load into a new frame");
if (expect.toplevelParent) {
is(details.parentFrameId, 0, "expect sub_frame to have top level parent");
is(details.frameAncestors.length, 1, "one ancestor for top sub_frame request");
} else {
ok(details.parentFrameId > 0, "expect sub_frame to have parent");
ok(details.frameAncestors.length > 1, "sub_frame has ancestors");
}
expect.subframeId = details.frameId;
expect.parentId = details.parentFrameId;
} else if (expect.sandboxed) {
is(details.documentUrl, undefined, "null principal documentUrl for sandboxed request");
} else {
// get the parent frame.
let purl = new URL(details.documentUrl);
let pfilename = purl.pathname.split("/").pop();
let parent = expected[pfilename];
is(details.frameId, parent.subframeId, "expect load in subframe");
is(details.parentFrameId, parent.parentId, "expect subframe parent");
}
return filename;
}
add_task(async function test_webRequest_main_frame() {
// Clear the image cache, since it gets in the way otherwise.
let imgTools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(SpecialPowers.Ci.imgITools);
let cache = imgTools.getImgCacheForDocument(document);
cache.clearCache(false);
await SpecialPowers.spawnChrome([], async () => {
Services.cache2.clear();
});
let extension = ExtensionTestUtils.loadExtension(extensionData);
await extension.startup();
let a = addLink(`file_simple_xhr.html?topframe=true&nocache=${Math.random()}`);
a.click();
let remaining = new Set(Object.keys(expected));
let totalExpectedCount = remaining.size;
for (let i = 0; i < totalExpectedCount; i++) {
info(`Waiting for request ${i + 1} out of ${totalExpectedCount}`);
info(`Expecting one of: ${Array.from(remaining)}`);
let details = await extension.awaitMessage("onBeforeRequest");
info(`Checking details for request ${i}: ${JSON.stringify(details)}`);
let filename = checkDetails(details);
ok(remaining.delete(filename), `Got only one request for ${filename}`);
}
await extension.awaitMessage("tab-created");
extension.sendMessage("close-tab");
await extension.awaitMessage("tab-closed");
await extension.unload();
});
</script>
</head>
<body>
<div id="test">Sample text</div>
</body>
</html>