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() { ...@@ -37,7 +37,7 @@ async function insertAboutBlankIframe() {
// Now create and add the script, but don't navigate anywhere (since we want // Now create and add the script, but don't navigate anywhere (since we want
// to stay on the initial about:blank). // to stay on the initial about:blank).
// We need to absolutize the URL to since about:blank doesn't have a base URL. // 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"); const script = iframe.contentDocument.createElement("script");
script.type = "module"; script.type = "module";
script.src = scriptURL; 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) { ...@@ -28,7 +28,7 @@ export async function insertIframe(host, header) {
* if the error event fires * if the error event fires
*/ */
export function navigateIframe(iframeEl, host, header) { export function navigateIframe(iframeEl, host, header) {
const url = getIframeURL(host, header); const url = getSendHeaderURL(host, header);
const waitPromise = waitForIframe(iframeEl, url); const waitPromise = waitForIframe(iframeEl, url);
iframeEl.src = url; iframeEl.src = url;
...@@ -54,6 +54,32 @@ export function waitForIframe(iframeEl, destinationForErrorMessage) { ...@@ -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 * 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 * the same agent cluster, by checking both that we can send a
...@@ -176,10 +202,59 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) { ...@@ -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 * Creates a promise_test() to check the value of the originIsolated getter in
* the given testFrame. * 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 {boolean} expected - The expected value for originIsolated.
* @param {string=} testLabelPrefix - A prefix used in the test names. This can * @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. * be omitted if the function is only used once in a test file.
...@@ -187,19 +262,20 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) { ...@@ -187,19 +262,20 @@ export function testDifferentAgentClusters(testFrames, testLabelPrefix) {
export function testGetter(testFrame, expected, testLabelPrefix) { export function testGetter(testFrame, expected, testLabelPrefix) {
const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `; const prefix = testLabelPrefix === undefined ? "" : `${testLabelPrefix}: `;
if (testFrame === self) { promise_test(async () => {
// Need to use promise_test() even though it's sync because we use if (testFrame === self) {
// promise_setup() in many tests.
promise_test(async () => {
assert_equals(self.originIsolated, expected); assert_equals(self.originIsolated, expected);
}, `${prefix}originIsolated must equal ${expected}`); } else if (typeof testFrame === "number") {
} else {
promise_test(async () => {
const frameWindow = frames[testFrame]; const frameWindow = frames[testFrame];
const result = await accessOriginIsolated(frameWindow); const result = await accessOriginIsolated(frameWindow);
assert_equals(result, expected); 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) { ...@@ -248,12 +324,15 @@ async function accessOriginIsolated(frameWindow) {
return waitForMessage(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); const url = new URL("send-origin-isolation-header.py", import.meta.url);
url.host = host; url.host = host;
if (header !== undefined) { if (header !== undefined) {
url.searchParams.set("header", header); url.searchParams.set("header", header);
} }
if (sendLoadedMessage) {
url.searchParams.set("send-loaded-message", "");
}
return url.href; return url.href;
} }
......
import { sendWasmModule } from "./helpers.mjs"; 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) => { 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) { if (e.data.constructor === WebAssembly.Module) {
e.source.postMessage("WebAssembly.Module message received", "*"); 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") { if (e.data.command === "set document.domain") {
document.domain = e.data.newDocumentDomain; document.domain = e.data.newDocumentDomain;
parent.postMessage("document.domain is set", "*"); e.source.postMessage("document.domain is set", "*");
} else if (e.data.command === "send WASM module") { } 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 destinationFrameWindow = parent.frames[e.data.indexIntoParentFrameOfDestination];
const whatHappened = await sendWasmModule(destinationFrameWindow); const whatHappened = await sendWasmModule(destinationFrameWindow);
parent.postMessage(whatHappened, "*"); parent.postMessage(whatHappened, "*");
...@@ -38,8 +50,6 @@ window.onmessage = async (e) => { ...@@ -38,8 +50,6 @@ window.onmessage = async (e) => {
} else { } else {
parent.postMessage("something wierd happened", "*"); 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", // We could also receive e.data === "WebAssembly.Module message received",
......
...@@ -17,5 +17,5 @@ def main(request, response): ...@@ -17,5 +17,5 @@ def main(request, response):
<title>Helper page for origin isolation tests</title> <title>Helper page for origin isolation tests</title>
<body> <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