<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/helper.js"></script>
<body>
<script>
let index = 0;
function wait_for_entries() {
// Generate a LoAF in the main world
setTimeout(function from_main() {
const deadline = performance.now() + 200;
while (performance.now() < deadline) {}
document.body.append("Timeout from main world");
}, 10);
// Wait until we receive the entry for this LoAF.
return new Promise(resolve => {
new PerformanceObserver((entries, observer) => {
if (entries.getEntriesByType("long-animation-frame").some(entry =>
entry.scripts.some(script => script.sourceFunctionName?.includes("from_main")))) {
resolve();
observer.disconnect();
}
}).observe({entryTypes: ["long-animation-frame"]});
});
}
function exec_isolated(func) {
if (!window.testRunner)
testFailed('window.testRunner not found');
const isolated_world_id = 1;
const execid = performance.now();
const complete = new Promise(resolve => new MutationObserver((entries, observer) => {
if (document.body.dataset.execid == execid) {
observer.disconnect();
resolve();
}
}).observe(document.body, {attributes: true}));
testRunner.evaluateScriptInIsolatedWorld(isolated_world_id, `
(async () => {
await (${func.toString()})();
document.body.dataset.execid = ${execid};
})();
`);
return complete;
}
function exec_with_src(func) {
return complete;
}
promise_test(async() => {
await new Promise(resolve => testRunner.evaluateScriptInOwnTask(`
const deadline = performance.now() + 200;
while (performance.now() < deadline) {}
`, "test-runner://forbidden", resolve));
await wait_for_entries();
const forbidden = performance.getEntriesByType("long-animation-frame")
.flatMap(e => e.scripts)
.find(script => script.sourceFunctionName?.includes("forbidden"));
assert_false(!!forbidden, `Forbidden script exposed ${forbidden?.sourceFunctionName}`);
}, "LoAF should only expose HTTP(s) scripts");
promise_test(async () => {
await exec_isolated(() => new Promise(resolve => {
setTimeout(function from_isolated() {
const deadline = performance.now() + 200;
while (performance.now() < deadline) {}
document.body.append("Timeout from isolated world");
document.body.append(document.createElement("br"));
resolve();
}, 0) }));
await wait_for_entries();
const forbidden = performance.getEntriesByType("long-animation-frame")
.flatMap(e => e.scripts)
.find(script => script.sourceFunctionName?.includes("from_isolated"));
assert_false(!!forbidden, `Forbidden script exposed ${forbidden?.sourceFunctionName} ${forbidden?.name}`);
}, "Test that LoAF doesn't expose scripts from isolated worlds for callbacks");
promise_test(async () => {
await exec_isolated(() => new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "/resources/dummy.xml");
xhr.addEventListener("load", function from_isolated() {
const deadline = performance.now() + 200;
while (performance.now() < deadline) {}
resolve();
});
xhr.send();
}));
await wait_for_entries();
const forbidden = performance.getEntriesByType("long-animation-frame")
.flatMap(e => e.scripts)
.find(script => script.sourceFunctionName?.includes("from_isolated"));
assert_false(!!forbidden, `Forbidden script exposed ${forbidden?.sourceFunctionName} ${forbidden?.name}`);
}, "Test that LoAF doesn't expose scripts from isolated worlds for events");
promise_test(async () => {
await exec_isolated(async () => {
fetch("/resources/dummy.xml").then(() => {
const deadline = performance.now() + 200;
while (performance.now() < deadline) {}
});
});
await wait_for_entries();
const forbidden = performance.getEntriesByType("long-animation-frame")
.flatMap(e => e.scripts)
.find(script => script.name === "Window.fetch.then");
assert_false(!!forbidden, `Forbidden script exposed ${forbidden?.name}`);
}, "Test that LoAF doesn't expose scripts from isolated worlds for promises");
</script>
</body>