Source code

Revision control

Copy as Markdown

Other Tools

// To use the functions below, be sure to include the following files in your
// test:
// - "/common/get-host-info.sub.js" to get the different origin values.
// - "common.js" to have the origins easily available.
// - "/common/dispatcher/dispatcher.js" for cross-origin messaging.
// - "/common/utils.js" for token().
function getBaseExecutorPath(origin) {
return origin + '/common/dispatcher/executor.html';
}
function getHeadersPipe(headers) {
const coop_header = headers.coop ?
`|header(Cross-Origin-Opener-Policy,${encodeURIComponent(headers.coop)})` : '';
const coep_header = headers.coep ?
`|header(Cross-Origin-Embedder-Policy,${encodeURIComponent(headers.coep)})` : '';
return coop_header + coep_header;
}
function getExecutorPath(uuid, origin, headers) {
return getBaseExecutorPath(origin) +
`?uuid=${uuid}` +
`&pipe=${getHeadersPipe(headers)}`;
}
function evaluate(target_token, script) {
const reply_token = token();
send(target_token, `send('${reply_token}', ${script});`);
return receive(reply_token);
}
// Return true if an opened iframe can access |property| on a stored
// window.popup object without throwing an error.
function iframeCanAccessProperty(iframe_token, property) {
const reply_token = token();
send(iframe_token,
`try {
const unused = window.popup['${property}'];
send('${reply_token}', 'true')
} catch (errors) {
send('${reply_token}', 'false')
}`);
return receive(reply_token);
}
// Returns the script necessary to open a popup, given the method in
// `popup_via`. Supported methods are 'window_open' that leverages
// window.open(), 'anchor' that creates an <a> HTML element and clicks on it,
// and 'form' that creates a form and submits it.
function popupOpeningScript(popup_via, popup_url, popup_origin, headers,
popup_token) {
if (popup_via === 'window_open')
return `window.popup = window.open('${popup_url}', '${popup_token}');`;
if (popup_via === 'anchor') {
return `
const anchor = document.createElement('a');
anchor.href = '${popup_url}';
anchor.rel = "opener";
anchor.target = '${popup_token}';
anchor.innerText = "anchor";
document.body.appendChild(anchor);
anchor.click();
`;
}
if (popup_via === "form") {
return `
const form = document.createElement("form");
form.action = '${getBaseExecutorPath(popup_origin.origin)}';
form.target = '${popup_token}';
form.method = 'GET';
const add_param = (name, value) => {
const input = document.createElement("input");
input.name = name;
input.value = value;
form.appendChild(input);
};
add_param("uuid", "${popup_token}");
add_param("pipe", "${getHeadersPipe(headers)}");
document.body.appendChild(form);
form.submit();
`;
}
assert_not_reached('Unrecognized popup opening method.');
}
function promise_test_parallel(promise, description) {
async_test(test => {
promise(test)
.then(() => test.done())
.catch(test.step_func(error => { throw error; }));
}, description);
};
// Verifies that a popup with origin `popup_origin` and headers `headers` has
// the expected `opener_state` after being opened from an iframe with origin
// `iframe_origin`.
function iframe_test(description, iframe_origin, popup_origin, headers,
expected_opener_state) {
for (const popup_via of ['window_open', 'anchor','form']) {
promise_test_parallel(async t => {
const iframe_token = token();
const popup_token = token();
const reply_token = token();
const frame = document.createElement("iframe");
const iframe_url = getExecutorPath(
iframe_token,
iframe_origin.origin,
{});
frame.src = iframe_url;
document.body.append(frame);
send(iframe_token, `send('${reply_token}', 'Iframe loaded');`);
assert_equals(await receive(reply_token), 'Iframe loaded');
const popup_url = getExecutorPath(
popup_token,
popup_origin.origin,
headers);
// We open popup and then ping it, it will respond after loading.
send(iframe_token, popupOpeningScript(popup_via, popup_url, popup_origin,
headers, popup_token));
send(popup_token, `send('${reply_token}', 'Popup loaded');`);
assert_equals(await receive(reply_token), 'Popup loaded');
// Make sure the popup and the iframe are removed once the test has run,
// keeping a clean state.
add_completion_callback(() => {
frame.remove();
send(popup_token, `close()`);
});
// Give some time for things to settle across processes etc. before
// proceeding with verifications.
await new Promise(resolve => { t.step_timeout(resolve, 500); });
// Verify that the opener is in the state we expect it to be in.
switch (expected_opener_state) {
case 'preserved': {
assert_equals(
await evaluate(popup_token, 'opener != null'), "true",
'Popup has an opener?');
assert_equals(
await evaluate(popup_token, `name === '${popup_token}'`), "true",
'Popup has a name?');
// When the popup was created using window.open, we've kept a handle
// and we can do extra verifications.
if (popup_via === 'window_open') {
assert_equals(
await evaluate(iframe_token, 'popup != null'), "true",
'Popup handle is non-null in iframe?');
assert_equals(
await evaluate(iframe_token, 'popup.closed'), "false",
'Popup appears closed from iframe?');
assert_equals(
await iframeCanAccessProperty(iframe_token, "document"),
popup_origin === iframe_origin ? "true" : "false",
'Iframe has dom access to the popup?');
assert_equals(
await iframeCanAccessProperty(iframe_token, "frames"), "true",
'Iframe has cross origin access to the popup?');
}
break;
}
case 'restricted': {
assert_equals(
await evaluate(popup_token, 'opener != null'), "true",
'Popup has an opener?');
assert_equals(
await evaluate(popup_token, `name === ''`), "true",
'Popup name is cleared?');
// When the popup was created using window.open, we've kept a handle
// and we can do extra verifications.
if (popup_via === 'window_open') {
assert_equals(
await evaluate(iframe_token, 'popup != null'), "true",
'Popup handle is non-null in iframe?');
assert_equals(
await evaluate(iframe_token, 'popup.closed'), "false",
'Popup appears closed from iframe?');
assert_equals(
await iframeCanAccessProperty(iframe_token, "document"), "false",
'Iframe has dom access to the popup?');
assert_equals(
await iframeCanAccessProperty(iframe_token, "frames"), "false",
'Iframe has cross origin access to the popup?');
assert_equals(
await iframeCanAccessProperty(iframe_token, "closed"), "true",
'Iframe has limited cross origin access to the popup?');
}
break;
}
case 'severed': {
assert_equals(await evaluate(popup_token, 'opener != null'), "false",
'Popup has an opener?');
assert_equals(
await evaluate(popup_token, `name === ''`), "true",
'Popup name is cleared?');
// When the popup was created using window.open, we've kept a handle
// and we can do extra verifications.
if (popup_via === 'window_open') {
assert_equals(
await evaluate(iframe_token, 'popup != null'), "true",
'Popup handle is non-null in iframe?');
assert_equals(
await evaluate(iframe_token, 'popup.closed'), "true",
'Popup appears closed from iframe?');
}
break;
}
case 'noopener': {
assert_equals(await evaluate(popup_token, 'opener != null'), "false",
'Popup has an opener?');
assert_equals(
await evaluate(popup_token, `name === ''`), "true",
'Popup name is cleared?');
// When the popup was created using window.open, we've kept a handle
// and we can do extra verifications.
if (popup_via === 'window_open') {
assert_equals(
await evaluate(iframe_token, 'popup == null'), "true",
'Popup handle is null in iframe?');
}
break;
}
default:
assert_not_reached('Unrecognized opener state: ' +
expected_opener_state);
}
}, `${description} with ${popup_via}`);
}
}