199 lines
8.1 KiB
HTML
199 lines
8.1 KiB
HTML
<!DOCTYPE html>
|
|
<title>Cross-Origin-Opener-Policy and a "javascript:" URL popup</title>
|
|
<meta charset="utf-8">
|
|
<meta name="timeout" content="long">
|
|
<meta name="variant" content="?1-2">
|
|
<meta name="variant" content="?3-4">
|
|
<meta name="variant" content="?5-6">
|
|
<meta name="variant" content="?7-8">
|
|
<meta name="variant" content="?9-10">
|
|
<meta name="variant" content="?11-12">
|
|
<meta name="variant" content="?13-14">
|
|
<meta name="variant" content="?15-16">
|
|
<meta name="variant" content="?17-last">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/common/dispatcher/dispatcher.js"></script>
|
|
<script src="/common/get-host-info.sub.js"></script>
|
|
<script src="/common/subset-tests.js"></script>
|
|
<script src="/common/utils.js"></script>
|
|
<script src="resources/common.js"></script>
|
|
|
|
<p>According to HTML's navigate algorithm, requests to <code>javascript:</code>
|
|
URLs should inherit the cross-origin opener policy of the active document. To
|
|
observe this, each subtest uses the following procedure.</p>
|
|
|
|
<ol>
|
|
<li>create popup with a given COOP (the <code>parentCOOP</code>)</li>
|
|
<li>navigate the popup to a <code>javascript:</code> URL (the new document is
|
|
expected to inherit the <code>parentCOOP</code>)</li>
|
|
<li>from the popup, create a second popup window with a given COOP (the
|
|
<code>childCOOP</code>)</li>
|
|
</ol>
|
|
|
|
<p>Both popup windows inspect state and report back to the test.</p>
|
|
|
|
<pre>
|
|
.---- test ----.
|
|
| open(https:) |
|
|
| parentCOOP | .----- subject -------.
|
|
| '---------> | --------. |
|
|
| | | v |
|
|
| | | assign(javascript:) |
|
|
| | | (COOP under test) |
|
|
| | | | |
|
|
| | | open(https:) |
|
|
| | | childCOOP | .- child -.
|
|
| | | '--------------> | |
|
|
| | '---------------------' '---------'
|
|
| | | |
|
|
| validate | <--status---+---------------------'
|
|
'--------------'
|
|
</pre>
|
|
|
|
<script>
|
|
'use strict';
|
|
|
|
function getExecutorPath(uuid, origin, coopHeader) {
|
|
const executorPath = '/common/dispatcher/executor.html?';
|
|
const coopHeaderPipe =
|
|
`|header(Cross-Origin-Opener-Policy,${encodeURIComponent(coopHeader)})`;
|
|
return origin + executorPath + `uuid=${uuid}` + '&pipe=' + coopHeaderPipe;
|
|
}
|
|
|
|
function assert_isolated(results) {
|
|
assert_equals(results.childName, '', 'child name');
|
|
assert_false(results.childOpener, 'child opener');
|
|
// The test subject's reference to the "child" window must report "closed"
|
|
// when COOP enforces isolation because the document initially created during
|
|
// the window open steps must be discarded when a new document object is
|
|
// created at the end of the navigation.
|
|
assert_true(results.childClosed, 'child closed');
|
|
}
|
|
|
|
function assert_not_isolated(results, expectedName) {
|
|
assert_equals(results.childName, expectedName, 'child name');
|
|
assert_true(results.childOpener, 'child opener');
|
|
assert_false(results.childClosed, 'child closed');
|
|
}
|
|
|
|
async function javascript_url_test(parentCOOP, childCOOP, origin, resultsVerification) {
|
|
promise_test(async t => {
|
|
const parentToken = token();
|
|
const childToken = token();
|
|
const responseToken = token();
|
|
|
|
const parentURL = getExecutorPath(
|
|
parentToken,
|
|
SAME_ORIGIN.origin,
|
|
parentCOOP);
|
|
const childURL = getExecutorPath(
|
|
childToken,
|
|
origin.origin,
|
|
childCOOP);
|
|
|
|
// Open a first popup, referred to as the parent popup, and wait for it to
|
|
// load.
|
|
window.open(parentURL);
|
|
send(parentToken, `send('${responseToken}', 'Done loading');`);
|
|
assert_equals(await receive(responseToken), 'Done loading');
|
|
|
|
// Make sure the parent popup is removed once the test has run, keeping a
|
|
// clean state.
|
|
add_completion_callback(() => {
|
|
send(parentToken, 'close');
|
|
});
|
|
|
|
// Navigate the popup to the javascript URL. It should inherit the current
|
|
// document's COOP. Because we're navigating to a page that is not an
|
|
// executor, we lose access to easy messaging, making things a bit more
|
|
// complicated. We use a predetermined scenario of communication that
|
|
// enables us to retrieve whether the child popup appears closed from the
|
|
// parent popup.
|
|
//
|
|
// Notes:
|
|
// - Splitting the script tag prevents HTML parsing to kick in.
|
|
// - The innermost double quotes need a triple backslash, because it goes
|
|
// through two rounds of consuming escape characters (\\\" -> \" -> ").
|
|
// - The javascript URL does not accept \n characters so we need to use
|
|
// a new template literal for each line.
|
|
send(parentToken,
|
|
`location.assign("javascript:'` +
|
|
// Include dispatcher.js to have access to send() and receive().
|
|
`<script src=\\\"/common/dispatcher/dispatcher.js\\\"></scr` + `ipt>` +
|
|
`<script> (async () => {` +
|
|
|
|
// Open the child popup and keep a handle to it.
|
|
`const w = open(\\\"${childURL}\\\", \\\"${childToken}\\\");` +
|
|
|
|
// We wait for the main frame to query the w.closed property.
|
|
`await receive(\\\"${parentToken}\\\");` +
|
|
`send(\\\"${responseToken}\\\", w.closed);` +
|
|
|
|
// Finally we wait for the cleanup indicating that this popup can be
|
|
// closed.
|
|
`await receive(\\\"${parentToken}\\\");` +
|
|
`close();` +
|
|
`})()</scr` + `ipt>'");`
|
|
);
|
|
|
|
// Make sure the javascript navigation ran, and the child popup was created.
|
|
send(childToken, `send('${responseToken}', 'Done loading');`);
|
|
assert_equals(await receive(responseToken), 'Done loading');
|
|
|
|
// Make sure the child popup is removed once the test has run, keeping a
|
|
// clean state.
|
|
add_completion_callback(() => {
|
|
send(childToken, `close()`);
|
|
});
|
|
|
|
// Give some time for things to settle across processes etc. before
|
|
// proceeding with verifications.
|
|
await new Promise(resolve => { t.step_timeout(resolve, 500); });
|
|
|
|
// Gather information about the child popup and verify that they match what
|
|
// we expect.
|
|
const results = {};
|
|
send(parentToken, 'query');
|
|
results.childClosed = await receive(responseToken) === 'true';
|
|
|
|
send(childToken, `send('${responseToken}', opener != null);`);
|
|
results.childOpener = await receive(responseToken) === 'true';
|
|
|
|
send(childToken, `send('${responseToken}', name);`);
|
|
results.childName = await receive(responseToken);
|
|
|
|
resultsVerification(results, childToken);
|
|
}, `navigation: ${origin.name}; ` + `parentCOOP: ${parentCOOP}; ` +
|
|
`childCOOP: ${childCOOP}`);
|
|
}
|
|
|
|
const tests = [
|
|
['unsafe-none', 'unsafe-none', SAME_ORIGIN, assert_not_isolated],
|
|
['unsafe-none', 'unsafe-none', SAME_SITE, assert_not_isolated],
|
|
['unsafe-none', 'same-origin-allow-popups', SAME_ORIGIN, assert_isolated],
|
|
['unsafe-none', 'same-origin-allow-popups', SAME_SITE, assert_isolated],
|
|
['unsafe-none', 'same-origin', SAME_ORIGIN, assert_isolated],
|
|
['unsafe-none', 'same-origin', SAME_SITE, assert_isolated],
|
|
['same-origin-allow-popups', 'unsafe-none', SAME_ORIGIN, assert_not_isolated],
|
|
['same-origin-allow-popups', 'unsafe-none', SAME_SITE, assert_not_isolated],
|
|
['same-origin-allow-popups', 'same-origin-allow-popups', SAME_ORIGIN, assert_not_isolated],
|
|
['same-origin-allow-popups', 'same-origin-allow-popups', SAME_SITE, assert_isolated],
|
|
['same-origin-allow-popups', 'same-origin', SAME_ORIGIN, assert_isolated],
|
|
['same-origin-allow-popups', 'same-origin', SAME_SITE, assert_isolated],
|
|
['same-origin', 'unsafe-none', SAME_ORIGIN, assert_isolated],
|
|
['same-origin', 'unsafe-none', SAME_SITE, assert_isolated],
|
|
['same-origin', 'same-origin-allow-popups', SAME_ORIGIN, assert_isolated],
|
|
['same-origin', 'same-origin-allow-popups', SAME_SITE, assert_isolated],
|
|
['same-origin', 'same-origin', SAME_ORIGIN, assert_not_isolated],
|
|
['same-origin', 'same-origin', SAME_SITE, assert_isolated],
|
|
].forEach(([parentCOOP, childCOOP, origin, expectation]) => {
|
|
subsetTest(
|
|
javascript_url_test,
|
|
parentCOOP,
|
|
childCOOP,
|
|
origin,
|
|
expectation);
|
|
});
|
|
|
|
</script>
|