Commit 49fd0e16 authored by Domenic Denicola's avatar Domenic Denicola Committed by Commit Bot

Origin isolation: add initial popup WPTs

This introduces a couple basic web platform tests for the popup cases.
The main content of this CL is generalizing the test infrastructure,
which previous was iframe-specific. Subsequent CLs will add a more
comprehensive suite of popup tests.

Bug: 1042415
Change-Id: I4f993783613f32cf9ebfb10e29973820e2fc9aad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364057Reviewed-by: default avatarJames MacLean <wjmaclean@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799695}
parent c6f7ce01
......@@ -37,7 +37,7 @@ async function insertAboutBlankIframe() {
// Now create and add the script, but don't navigate anywhere (since we want
// to stay on the initial about:blank).
// We need to absolutize the URL to since about:blank doesn't have a base URL.
const scriptURL = (new URL("./resources/child-frame-script.mjs", import.meta.url)).href;
const scriptURL = (new URL("./resources/send-header-page-script.mjs", import.meta.url)).href;
const script = iframe.contentDocument.createElement("script");
script.type = "module";
script.src = scriptURL;
......
<!DOCTYPE html>
<meta charset="utf-8">
<title>Opener is not isolated, openee is isolated, openee is different-origin to the opener because of a port mismatch</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script type="module">
import {
openWindow,
testOpenedWindowIsInADifferentAgentCluster,
testGetter
} from "../resources/helpers.mjs";
let openee;
promise_setup(async () => {
openee = await openWindow("{{hosts[][]}}:{{ports[https][1]}}", "?1");
});
// Since they're different-origin, the openee's isolation request is respected,
// so the opener ends up in the site-keyed agent cluster and the openee in the
// origin-keyed one.
testOpenedWindowIsInADifferentAgentCluster(() => openee);
testGetter(self, false, "opener");
testGetter(() => openee, true, "openee");
</script>
<!DOCTYPE html>
<meta charset="utf-8">
<title>Opener is not isolated, openee is isolated, openee is same-origin to the opener</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script type="module">
import {
openWindow,
testOpenedWindowIsInSameAgentCluster,
testGetter
} from "../resources/helpers.mjs";
let openee;
promise_setup(async () => {
openee = await openWindow("{{hosts[][]}}", "?1");
});
// Since they're same-origin, and the opener loaded without isolation, the
// child's request for isolation gets ignored, and both end up site-keyed.
testOpenedWindowIsInSameAgentCluster(() => openee);
testGetter(self, false, "opener");
testGetter(() => openee, false, "openee");
</script>
......@@ -28,7 +28,7 @@ export async function insertIframe(host, header) {
* if the error event fires
*/
export function navigateIframe(iframeEl, host, header) {
const url = getIframeURL(host, header);
const url = getSendHeaderURL(host, header);
const waitPromise = waitForIframe(iframeEl, url);
iframeEl.src = url;
......@@ -54,6 +54,32 @@ export function waitForIframe(iframeEl, destinationForErrorMessage) {
});
}
/**
* Opens a new window usable for origin isolation testing, and returns a promise
* fulfilled when the window is loaded and its document.domain is set. The
* window will point to the send-origin-isolation-header.py file, on the
* designated host.
*
* The opened window will be automatically closed when all the tests complete.
* @param {string} host - The host used to calculate the window's URL
* @param {string=} header - The value of the Origin-Isolation header that the
* iframe will set. Omit this to set no header.
* @returns {WindowProxy} The created window
*/
export async function openWindow(host, header) {
const url = getSendHeaderURL(host, header, { sendLoadedMessage: true });
const openedWindow = window.open(url);
add_completion_callback(() => openedWindow.close());
const whatHappened = await waitForMessage(openedWindow);
assert_equals(whatHappened, "loaded");
await setBothDocumentDomains(openedWindow);
return openedWindow;
}
/**
* Expands into a pair of promise_test() calls to ensure that two Windows are in
* the same agent cluster, by checking both that we can send a
......@@ -176,10 +202,59 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) {
}
}
/**
* Expands into a pair of promise_test() calls to ensure that the given window,
* opened by window.open(), is in a different agent cluster from the current
* (opener) window.
* @param {function} openedWindowGetter - A function that returns the opened
* window
*/
export function testOpenedWindowIsInADifferentAgentCluster(openedWindowGetter) {
promise_test(async () => {
const whatHappened = await sendWasmModule(openedWindowGetter());
assert_equals(whatHappened, "messageerror");
}, `messageerror event must occur`);
promise_test(async () => {
assert_throws_dom("SecurityError", DOMException, () => {
openedWindowGetter().document;
});
assert_throws_dom("SecurityError", DOMException, () => {
openedWindowGetter().location.href;
});
}, `setting document.domain must not give sync access`);
}
/**
* Expands into a pair of promise_test() calls to ensure that the given window,
* opened by window.open(), is in the same agent cluster as the current
* (opener) window.
* @param {function} openedWindowGetter - A function that returns the opened
* window
*/
export function testOpenedWindowIsInSameAgentCluster(openedWindowGetter) {
promise_test(async () => {
const whatHappened = await sendWasmModule(openedWindowGetter());
assert_equals(whatHappened, "WebAssembly.Module message received");
}, `message event must occur`);
promise_test(async () => {
// Must not throw
openedWindowGetter().document;
// Must not throw
openedWindowGetter().location.href;
}, `setting document.domain must give sync access`);
}
/**
* Creates a promise_test() to check the value of the originIsolated getter in
* the given testFrame.
* @param {Window|number} testFrame - Either self, or a frame index to test.
* @param {Window|number|function} testFrame - Either self, or a frame index to
test, or a function that returns a windwo to test.
* @param {boolean} expected - The expected value for originIsolated.
* @param {string=} testLabelPrefix - A prefix used in the test names. This can
* be omitted if the function is only used once in a test file.
......@@ -187,19 +262,20 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) {
export function testGetter(testFrame, expected, testLabelPrefix) {
const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `;
if (testFrame === self) {
// Need to use promise_test() even though it's sync because we use
// promise_setup() in many tests.
promise_test(async () => {
promise_test(async () => {
if (testFrame === self) {
assert_equals(self.originIsolated, expected);
}, `${prefix}originIsolated must equal ${expected}`);
} else {
promise_test(async () => {
} else if (typeof testFrame === "number") {
const frameWindow = frames[testFrame];
const result = await accessOriginIsolated(frameWindow);
assert_equals(result, expected);
}, `${prefix}originIsolated must equal ${expected}`);
}
} else {
assert_equals(typeof testFrame, "function",
"testFrame argument must be self, a number, or a function");
const result = await accessOriginIsolated(testFrame());
assert_equals(result, expected);
}
}, `${prefix}originIsolated must equal ${expected}`);
}
/**
......@@ -248,12 +324,15 @@ async function accessOriginIsolated(frameWindow) {
return waitForMessage(frameWindow);
}
function getIframeURL(host, header) {
function getSendHeaderURL(host, header, { sendLoadedMessage = false } = {}) {
const url = new URL("send-origin-isolation-header.py", import.meta.url);
url.host = host;
if (header !== undefined) {
url.searchParams.set("header", header);
}
if (sendLoadedMessage) {
url.searchParams.set("send-loaded-message", "");
}
return url.href;
}
......
import { sendWasmModule } from "./helpers.mjs";
// This is done for the window.open() case. For <iframe>s we use the
// <iframe> element's load event instead.
const usp = new URLSearchParams(location.href);
if (usp.has("send-loaded-message")) {
opener.postMessage("loaded", "*");
}
window.onmessage = async (e) => {
// These could come from the parent or siblings.
// These could come from the parent, opener, or siblings.
if (e.data.constructor === WebAssembly.Module) {
e.source.postMessage("WebAssembly.Module message received", "*");
}
// These only come from the parent.
// These could come from the parent or opener.
if (e.data.command === "set document.domain") {
document.domain = e.data.newDocumentDomain;
parent.postMessage("document.domain is set", "*");
} else if (e.data.command === "send WASM module") {
e.source.postMessage("document.domain is set", "*");
} else if (e.data.command === "get originIsolated") {
e.source.postMessage(self.originIsolated, "*");
}
// These only come from the parent.
if (e.data.command === "send WASM module") {
const destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination];
const whatHappened = await sendWasmModule(destinationFrameWindow);
parent.postMessage(whatHappened, "*");
......@@ -38,8 +50,6 @@ window.onmessage = async (e) => {
} else {
parent.postMessage("something wierd happened", "*");
}
} else if (e.data.command === "get originIsolated") {
parent.postMessage(self.originIsolated, "*");
}
// We could also receive e.data === "WebAssembly.Module message received",
......
......@@ -17,5 +17,5 @@ def main(request, response):
<title>Helper page for origin isolation tests</title>
<body>
<script type="module" src="child-frame-script.mjs"></script>
<script type="module" src="send-header-page-script.mjs"></script>
"""
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment