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
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3750822
Reviewed-by: Ian Clelland <iclelland@chromium.org>
Commit-Queue: Fergal Daly <fergal@chromium.org>
Reviewed-by: Mason Freed <masonf@chromium.org>
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Dominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1032917}
NOKEYCHECK=True
GitOrigin-RevId: 5c436e20b6d923241eadd2afe8b846ed32d46eea
  • Loading branch information
fergald authored and Copybara-Service committed Aug 9, 2022
1 parent 92f9a6b commit 1c1d5c8
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 0 deletions.
1 change: 1 addition & 0 deletions blink/public/devtools_protocol/browser_protocol.pdl
Original file line number Diff line number Diff line change
Expand Up @@ -7156,6 +7156,7 @@ domain Page
storage-access-api
sync-xhr
trust-token-redemption
unload
usb
vertical-scroll
web-share
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ enum PermissionsPolicyFeature {
// API.
kSharedStorage = 103,

// Controls use of unload handlers.
kUnload = 104,

// Don't change assigned numbers of any item, and don't reuse removed slots.
// Add new features at the end of the enum.
// Also, run update_permissions_policy_enum.py in
Expand Down
9 changes: 9 additions & 0 deletions blink/renderer/core/dom/events/event_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,15 @@ bool EventTarget::AddEventListenerInternal(
if (options->hasSignal() && options->signal()->aborted())
return false;

// Consider `Permissions-Policy: unload`.
if (event_type == event_type_names::kUnload &&
RuntimeEnabledFeatures::PermissionsPolicyUnloadEnabled() &&
!GetExecutionContext()->IsFeatureEnabled(
mojom::blink::PermissionsPolicyFeature::kUnload,
ReportOptions::kReportOnFailure)) {
return false;
}

// Unload/Beforeunload handlers are not allowed in fenced frames.
if (event_type == event_type_names::kUnload ||
event_type == event_type_names::kBeforeunload) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@
permissions_policy_name: "usb",
depends_on: ["WebUSB"],
},
{
name: "Unload",
permissions_policy_name: "unload",
feature_default: "EnableForAll",
depends_on: ["PermissionsPolicyUnload"],
},
{
name: "VerticalScroll",
permissions_policy_name: "vertical-scroll",
Expand Down
5 changes: 5 additions & 0 deletions blink/renderer/platform/runtime_enabled_features.json5
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,11 @@
name: "Permissions",
status: "stable",
},
// See https://crbug.com/1324111
{
name: "PermissionsPolicyUnload",
status: "experimental"
},
{
name: "PermissionsRequestRevoke",
status: "experimental",
Expand Down
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 is cross-site then we have to return
// to the original origin in order to read the recorded unload.
second.historyBack();

// 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
@@ -0,0 +1,20 @@
// 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

'use strict';

// Check that unload is allowed by policy in main frame and subframe by default.
promise_test(async t => {
const rcHelper =
new RemoteContextHelper({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});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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

'use strict';

// Check that unload is allowed by policy in main but can be disabled in the
// subframe.
promise_test(async t => {
const rcHelper =
new RemoteContextHelper({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({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,22 @@
// 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

'use strict';

// Check that unload can be disabled by policy in main frame and subframe.
promise_test(async t => {
const rcHelper =
new RemoteContextHelper({scripts: ['./resources/unload-helper.js']});
// In the same browsing context group to ensure BFCache is not used.
const main = await rcHelper.addWindow(
{headers: [['Permissions-Policy', 'unload=()']]},
);
const subframe = await main.addIframe();
await assertWindowRunsUnload(subframe, 'subframe', {shouldRunUnload: false});
await assertWindowRunsUnload(main, 'main', {shouldRunUnload: false});
});
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ serial
shared-autofill
storage-access-api
sync-xhr
unload
usb
vertical-scroll
window-placement
Expand Down

0 comments on commit 1c1d5c8

Please sign in to comment.