Skip to content

Commit

Permalink
Adds unload to Permissions-Policy.
Browse files Browse the repository at this point in the history
This policy controls whether unload handlers can be set.

See w3c/webappsec-permissions-policy#444

Bug: 1324111
Change-Id: Ia7c418e7ca3131f5102fde407011e00048a94182
  • Loading branch information
fergald authored and chromium-wpt-export-bot committed Jul 27, 2022
1 parent b10070d commit e898e95
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 35 deletions.
59 changes: 29 additions & 30 deletions permissions-policy/experimental-features/resources/common.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const url_base = "/permissions-policy/experimental-features/resources/";
const url_base = '/permissions-policy/experimental-features/resources/';
window.messageResponseCallback = null;

function setFeatureState(iframe, feature, origins) {
iframe.setAttribute("allow", `${feature} ${origins};`);
iframe.setAttribute('allow', `${feature} ${origins};`);
}

// Returns a promise which is resolved when the <iframe> is navigated to |url|
// and "load" handler has been called.
function loadUrlInIframe(iframe, url) {
return new Promise((resolve) => {
iframe.addEventListener("load", resolve);
iframe.addEventListener('load', resolve);
iframe.src = url;
});
}
Expand All @@ -19,7 +19,7 @@ function loadUrlInIframe(iframe, url) {
function sendMessageAndGetResponse(target, message) {
return new Promise((resolve) => {
window.messageResponseCallback = resolve;
target.postMessage(message, "*");
target.postMessage(message, '*');
});
}

Expand All @@ -31,28 +31,27 @@ function onMessage(e) {
}
}

window.addEventListener("message", onMessage);
window.addEventListener('message', onMessage);

// Waits for |load_timeout| before resolving the promise. It will resolve the
// promise sooner if a message event with |e.data.id| of |id| is received.
// In such a case the response is the contents of the message |e.data.contents|.
// Otherwise, returns false (when timeout occurs).
function waitForMessageOrTimeout(t, id, load_timeout) {
return new Promise((resolve) => {
window.addEventListener(
"message",
(e) => {
if (!e.data || e.data.id !== id)
return;
resolve(e.data.contents);
}
);
t.step_timeout(() => { resolve(false); }, load_timeout);
window.addEventListener('message', (e) => {
if (!e.data || e.data.id !== id)
return;
resolve(e.data.contents);
});
t.step_timeout(() => {
resolve(false);
}, load_timeout);
});
}

function createIframe(container, attributes) {
var new_iframe = document.createElement("iframe");
var new_iframe = document.createElement('iframe');
for (attr_name in attributes)
new_iframe.setAttribute(attr_name, attributes[attr_name]);
container.appendChild(new_iframe);
Expand All @@ -62,33 +61,33 @@ function createIframe(container, attributes) {
// Returns a promise which is resolved when |load| event is dispatched for |e|.
function wait_for_load(e) {
return new Promise((resolve) => {
e.addEventListener("load", resolve);
e.addEventListener('load', resolve);
});
}

setup(() => {
window.reporting_observer_instance = new ReportingObserver((reports, observer) => {
if (window.reporting_observer_callback) {
reports.forEach(window.reporting_observer_callback);
}
}, {types: ["permissions-policy-violation"]});
window.reporting_observer_instance =
new ReportingObserver((reports, observer) => {
if (window.reporting_observer_callback) {
reports.forEach(window.reporting_observer_callback);
}
}, {types: ['permissions-policy-violation']});
window.reporting_observer_instance.observe();
window.reporting_observer_callback = null;
});

// Waits for a violation in |feature| and source file containing |file_name|.
function wait_for_violation_in_file(feature, file_name) {
return new Promise( (resolve) => {
return new Promise((resolve) => {
assert_equals(null, window.reporting_observer_callback);
window.reporting_observer_callback = (report) => {
var feature_match = (feature === report.body.featureId);
var file_name_match =
!file_name ||
(report.body.sourceFile.indexOf(file_name) !== -1);
if (feature_match && file_name_match) {
window.reporting_observer_callback = null;
resolve(report);
}
var feature_match = (feature === report.body.featureId);
var file_name_match =
!file_name || (report.body.sourceFile.indexOf(file_name) !== -1);
if (feature_match && file_name_match) {
window.reporting_observer_callback = null;
resolve(report);
}
};
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Code used by controlling frame of the unload policy tests.

const MAIN_FRAME = 'main';
const SUBFRAME = 'sub';

async function isUnloadAllowed(remoteContextWrapper) {
return remoteContextWrapper.executeScript(() => {
return document.featurePolicy.allowsFeature('unload');
});
}

// Checks whether a frame runs unload handlers.
// This checks the policy directly and also installs an unload handler and
// navigates the frame checking that the handler ran.
async function assertWindowRunsUnload(
remoteContextWrapper, name, {shouldRunUnload}) {
const maybeNot = shouldRunUnload ? '' : 'not ';
assert_equals(
await isUnloadAllowed(remoteContextWrapper), shouldRunUnload,
`${name}: unload in ${name} should ${maybeNot}be allowed`);

// Set up recording of whether unload handler ran.
await remoteContextWrapper.executeScript((name) => {
localStorage.setItem(name, 'did not run');
addEventListener('unload', () => localStorage.setItem(name, 'did run'));
}, [name]);

// Navigate away and then back.
const second = await remoteContextWrapper.navigateToNew();
// Navigating back ensures that the unload has completed.
// Also if the navigation if cross-site then we have to return
// to the original origin in order to read the recorded unload.
await historyBack(second);

// Check that unload handlers ran as expected.
const recordedUnload = await remoteContextWrapper.executeScript(
(name) => localStorage.getItem(name), [name]);
assert_equals(
recordedUnload, `did ${maybeNot}run`,
`${name}: unload should ${maybeNot}have run`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ function isEmptyRect(rect) {
function rects_intersect(rect1, rect2) {
if (isEmptyRect(rect1) || isEmptyRect(rect2))
return false;
return rect1.x < rectMaxX(rect2) &&
rect2.x < rectMaxX(rect1) &&
rect1.y < rectMaxY(rect2) &&
rect2.y < rectMaxY(rect1);
return rect1.x < rectMaxX(rect2) && rect2.x < rectMaxX(rect1) &&
rect1.y < rectMaxY(rect2) && rect2.y < rectMaxY(rect1);
}

function rectToString(rect) {
return `Location: (${rect.x}, ${rect.y}) Size: (${rect.width}, ${rect.height})`;
return `Location: (${rect.x}, ${rect.y}) Size: (${rect.width}, ${
rect.height})`;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// META: title='unload' Policy : allowed by default
// META: script=/common/dispatcher/dispatcher.js
// META: script=/common/utils.js
// META: script=/resources/testharness.js
// META: script=/resources/testharnessreport.js
// META:
// script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: script=./resources/unload-helper.js

// Check that unload is allowed by policy in main frame and subframe by default.
promise_test(async t => {
const rcHelper = new RemoteContextHelper(
{remoteContextConfig: {scripts: ['./resources/unload-helper.js']}});
// In the same browsing context group to ensure BFCache is not used.
const main = await rcHelper.addWindow();
const subframe = await main.addIframe();
await assertWindowRunsUnload(subframe, 'subframe', {shouldRunUnload: true});
await assertWindowRunsUnload(main, 'main', {shouldRunUnload: true});
}, '\'unload\' Policy : allowed by default');
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// META: title='unload' Policy : allowed in main frame but disallowed in
// subframe META: script=/common/dispatcher/dispatcher.js META:
// script=/common/utils.js META: script=/resources/testharness.js META:
// script=/resources/testharnessreport.js META:
// script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: script=./resources/unload-helper.js

// Check that unload is allowed by policy in main but can be disabled in the
// subframe.
promise_test(async t => {
const rcHelper = new RemoteContextHelper(
{remoteContextConfig: {scripts: ['./resources/unload-helper.js']}});
// In the same browsing context group to ensure BFCache is not used.
const main = await rcHelper.addWindow();
const subframe = await main.addIframe({
extraRemoteContextConfig: {headers: [['Permissions-Policy', 'unload=()']]}
});
await assertWindowRunsUnload(subframe, 'subframe', {shouldRunUnload: false});
await assertWindowRunsUnload(main, 'main', {shouldRunUnload: true});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// META: title='unload' Policy : disallowed when header is ()
// META: script=/common/dispatcher/dispatcher.js
// META: script=/common/utils.js
// META: script=/resources/testharness.js
// META: script=/resources/testharnessreport.js
// META:
// script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: script=./resources/unload-helper.js

// Check that unload can be disabled by policy in main frame and subframe.
promise_test(async t => {
const rcHelper = new RemoteContextHelper(
{remoteContextConfig: {scripts: ['./resources/unload-helper.js']}});
// In the same browsing context group to ensure BFCache is not used.
const main = await rcHelper.addWindow({
extraRemoteContextConfig: {headers: [['Permissions-Policy', 'unload=()']]}
});
const subframe = await main.addIframe();
await assertWindowRunsUnload(subframe, 'subframe', {shouldRunUnload: false});
await assertWindowRunsUnload(main, 'main', {shouldRunUnload: false});
});

0 comments on commit e898e95

Please sign in to comment.