diff options
Diffstat (limited to 'testing/web-platform/tests/permissions-policy/experimental-features')
34 files changed, 1485 insertions, 0 deletions
diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/focus-without-user-activation-disabled-tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/focus-without-user-activation-disabled-tentative.html new file mode 100644 index 0000000000..3980fd1219 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/focus-without-user-activation-disabled-tentative.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/common.js"></script> +<title> 'focus-without-user-activation' Policy : Correctly block automatic focus when policy disabled +</title> +<body> +<script> + "use strict" + // Note: According to html spec: https://html.spec.whatwg.org/#attr-fe-autofocus, + // topDocument's autofocus processed flag initially is false and is set to true + // after flushing autofocus candidates, i.e. flush of autofocus candidates + // only happens once per page load. + // In order to test the behaviour with both focus-without-user-activation on and off: + // two test files are necessary: + // - focus-without-user-activation-disabled-tentative.html + // - focus-without-user-activation-enabled-tentative.sub.html + + // Use same origin url here because when iframe document has cross origin + // url, autofocus will be blocked by default with following console error: + // "Blocked autofocusing on a form control in a cross-origin subframe." + const url = "/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html"; + + function subframe_focused(subframe, event_name, timeout) { + return new Promise(resolve => { + window.onmessage = m => resolve(m.data.focused); + subframe.contentWindow.postMessage({ + event: event_name, + timeout: timeout + }, "*"); + }); + } + + promise_test( async (instance) => { + const frame = createIframe(document.body, { + sandbox: "allow-scripts allow-same-origin", + allow: "focus-without-user-activation 'none'", + src: url + }); + + await wait_for_load(frame); + assert_false(await subframe_focused(frame, "autofocus", 400), "'autofocus' should not work."); + window.focus(); // Reset focus state in subframe. + assert_false(await subframe_focused(frame, "focus-input", 400), "'element.focus' should not work."); + window.focus(); // Reset focus state in subframe. + assert_false(await subframe_focused(frame, "focus-window", 400), "'window.focus' should not work."); + window.focus(); // Reset focus state in subframe. + }, "When the policy is disabled, 'autofocus' and scripted focus do not focus " + + "the document."); +</script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html new file mode 100644 index 0000000000..5722947f86 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/common.js"></script> +<title> 'focus-without-user-activation' Policy : Correctly block automatic focus when policy disabled +</title> +<body> +<script> + "use strict" + // Note: According to html spec: https://html.spec.whatwg.org/#attr-fe-autofocus, + // topDocument's autofocus processed flag initially is false and is set to true + // after flushing autofocus candidates, i.e. flush of autofocus candidates + // only happens once per page load. + // In order to test the behaviour with both focus-without-user-activation on and off: + // two test files are necessary: + // - focus-without-user-activation-disabled-tentative.html + // - focus-without-user-activation-enabled-tentative.sub.html + + // Cross origin subframe should not be able to use autofocus to steal focus + // from main frame by default. However, with focus-without-user-activation + // enabled for subframe, subframe should be able to autofocus. + const url = "http://{{hosts[alt][www1]}}:{{ports[http][0]}}/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html"; + + function subframe_focused(subframe, event_name, timeout) { + return new Promise(resolve => { + window.onmessage = m => resolve(m.data.focused); + subframe.contentWindow.postMessage({ + event: event_name, + timeout: timeout + }, "*"); + }); + } + + promise_test( async (instance) => { + const frame = createIframe(document.body, { + sandbox: "allow-scripts allow-same-origin", + allow: "focus-without-user-activation *", + src: url + }); + + await wait_for_load(frame); + assert_true(await subframe_focused(frame, "autofocus"), "'autofocus' should work."); + window.focus(); // Reset focus state in subframe. + assert_true(await subframe_focused(frame, "focus-input"), "'element.focus' should work."); + window.focus(); // Reset focus state in subframe. + assert_true(await subframe_focused(frame, "focus-window"), "'window.focus' should work."); + window.focus(); // Reset focus state in subframe. + }, "When the policy is enabled, 'autofocus' and scripted focus do focus " + + "the document."); +</script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html new file mode 100644 index 0000000000..211ca7445d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + test(function() { + var wildcard_origin = 'https://*:{{ports[https][0]}}'; + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [wildcard_origin]); + assert_true(document.featurePolicy.allowsFeature('fullscreen')); + }, 'Test wildcard host policy works as expected'); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html.sub.headers new file mode 100644 index 0000000000..81d0de836e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https://*:{{ports[https][0]}}") diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html new file mode 100644 index 0000000000..adb657023e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + test(function() { + var wildcard_origin = 'https://{{domains[]}}:*'; + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [wildcard_origin]); + assert_true(document.featurePolicy.allowsFeature('fullscreen')); + }, 'Test wildcard port policy works as expected'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html.sub.headers new file mode 100644 index 0000000000..b813167ee6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https://{{domains[]}}:*") diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html new file mode 100644 index 0000000000..eab3e8f697 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + test(function() { + var wildcard_origin = 'https:'; + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [wildcard_origin]); + assert_true(document.featurePolicy.allowsFeature('fullscreen')); + }, 'Test scheme only policy works as expected'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html.sub.headers new file mode 100644 index 0000000000..14d0868f2b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https:") diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-default-permissions-policy.tentative.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-default-permissions-policy.tentative.https.sub.html new file mode 100644 index 0000000000..4962b42721 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-default-permissions-policy.tentative.https.sub.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title>Test that private state token redemption is enabled/disabled according to the permissions policy</title> + +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Default "private-state-token-redemption" permissions policy ["self"]'; + + test(() => { + try { + // The permissions policy gates redemption and signing via both the Fetch + // and XHR interfaces. + new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-redemption" + } + }); + new Request("https://destination.example/", { + privateToken: { + version: 1, + operation: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + } + }); + + const redemption_xhr = new XMLHttpRequest(); + redemption_xhr.open("GET", "https://issuer.example/"); + redemption_xhr.setPrivateToken({ + version: 1, + operation: "token-redemption" + }); + + const signing_xhr = new XMLHttpRequest(); + signing_xhr.open("GET", "https://destination.example/"); + signing_xhr.setPrivateToken({ + version: 1, + operation: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + }); + } catch (e) { + assert_unreached(); + } + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('Private state token redemption', t, same_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 4, desc); + }); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('Private state token redemption', t, cross_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }); + }, header + ' disallows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-supported-by-permissions-policy.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-supported-by-permissions-policy.tentative.html new file mode 100644 index 0000000000..0399f167fb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-supported-by-permissions-policy.tentative.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<title>Test that private state token redemption is advertised in the feature list</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + test(() => { + assert_in_array('private-state-token-redemption', document.featurePolicy.features()); + }, 'document.featurePolicy.features should advertise private-state-token-redemption.'); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/common.js b/testing/web-platform/tests/permissions-policy/experimental-features/resources/common.js new file mode 100644 index 0000000000..308f787da6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/common.js @@ -0,0 +1,94 @@ +const url_base = "/permissions-policy/experimental-features/resources/"; +window.messageResponseCallback = null; + +function setFeatureState(iframe, 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.src = url; + }); +} + +// Posts |message| to |target| and resolves the promise with the response coming +// back from |target|. +function sendMessageAndGetResponse(target, message) { + return new Promise((resolve) => { + window.messageResponseCallback = resolve; + target.postMessage(message, "*"); + }); +} + + +function onMessage(e) { + if (window.messageResponseCallback) { + window.messageResponseCallback(e.data); + window.messageResponseCallback = null; + } +} + +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); + }); +} + +function createIframe(container, attributes) { + var new_iframe = document.createElement("iframe"); + for (attr_name in attributes) + new_iframe.setAttribute(attr_name, attributes[attr_name]); + container.appendChild(new_iframe); + return new_iframe; +} + +// 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); + }); +} + +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.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) => { + 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); + } + }; + }); +} diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html new file mode 100644 index 0000000000..3d5aab95ad --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html @@ -0,0 +1,47 @@ +<!doctype html> +<input autofocus onfocus="autofocus_onfocus()"/> +<script> + let autofocused = false; + function autofocus_onfocus() { + autofocused = true; + } + + /** + * @param target object: Target to call |focus()| with. + * @param timeout integer | undefined: Timeout to wait for the focus event. + * If unspecified, a timeout will not be set. + * @param focus_target boolean | undefined: Wether to focus the target after + * listening for |onfocus| event. + */ + function wait_focus_event(target, timeout, focus_target) { + return new Promise((resolve) => { + if (timeout) + setTimeout(() => resolve(false), timeout); + + target.onfocus = () => resolve(true); + if (focus_target) + target.focus(); + }); + } + + function post_result(destination, result) { + destination.postMessage({focused: result}, "*"); + } + + window.addEventListener("message", (e) => { + if (e.data.event === "autofocus") { + if (autofocused) + post_result(e.source, true); + + wait_focus_event(document.querySelector("input"), e.data.timeout) + .then(result => post_result(e.source, result)); + } else if (e.data.event === "focus-window") { + wait_focus_event(window, e.data.timeout, true /* focus_target */) + .then(result => post_result(e.source, result)); + } else if (e.data.event === "focus-input") { + const input_element = document.querySelector("input"); + wait_focus_event(input_element, e.data.timeout, true /* focus_target */) + .then(result => post_result(e.source, result)); + } +}); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload-contents.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload-contents.html new file mode 100644 index 0000000000..a6e98c24e6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload-contents.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<body> + <p>This page is lazyloaded.</p> + <script> + let query_index = window.location.href.indexOf("?id="); + let id = window.location.href.substr(query_index + 4); + window.addEventListener("load", () => { + let p = document.querySelector("p"); + let contents = p ? p.innerHTML : "null"; + window.parent.postMessage({"id": id, "contents": contents}, "*"); + }); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload.png b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload.png Binary files differnew file mode 100644 index 0000000000..fd3da53a29 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload.png diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html new file mode 100644 index 0000000000..7a055f0e7b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<script> + 'use strict'; + + window.onload = function() { + // When the private-state-token-redemption permissions policy is enabled, redemption + // and signing ("send-redemption-record") should both be available; when it's disabled, + // they should both be unavailable. Send the number of available operations + // upstream in order to enforce this in assertions. + let num_enabled = 4; + try { + new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-redemption" + } + }); + } catch (e) { + num_enabled--; + } + try { + new Request("https://destination.example/", { + privateToken: { + version: 1, + operation: "send-redemption-record", + issuers: ["https://issuer.example/"] + } + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "token-redemption" + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://destination.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "send-redemption-record", + issuers: ["https://issuer.example/"] + }); + } catch (e) { + num_enabled--; + } + + parent.postMessage({ + type: 'availability-result', + num_operations_enabled: num_enabled, + }, '*'); + } +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/unload-helper.js b/testing/web-platform/tests/permissions-policy/experimental-features/resources/unload-helper.js new file mode 100644 index 0000000000..9739ead69d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/unload-helper.js @@ -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`); +} diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html new file mode 100644 index 0000000000..9f78ea4bc2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<style> + body, html { + height: 100%; + width: 100%; + } + #spacer { + width: 1500px; + height: 1500px; + background-color: red; + } +</style> +<body> + <p>This page is scrollable.</p> + <div id="spacer"></div> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html new file mode 100644 index 0000000000..fd432b33f6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html @@ -0,0 +1,13 @@ +<!doctype html> +<title>Ref: vertical-scroll test for scrollbar</title> +<iframe src="/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html"></iframe> +<script> + let iframe = document.querySelector("iframe"); + let overflow_y = "visible"; + if (window.location.search.indexOf("no-vertical-scrollbar") !== -1) + overflow_y = "hidden" + iframe.addEventListener("load", () => { + iframe.contentDocument.body.style.overflowY = overflow_y; + }); +</script> + diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollintoview.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollintoview.html new file mode 100644 index 0000000000..7bed27c260 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollintoview.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<style> +html, body, #container { + width: 100%; + height: 100%; +} + +#spacer { + width: 200%; + height: 200%; +} +</style> +<div id="container"> + <div id="spacer"></div> + <button>Element To Scroll</button> +</div> +<script> + window.addEventListener('message', onMessageReceived); + + function scrollingElementBounds() { + var rect = document.querySelector("button").getBoundingClientRect(); + return { + x: rect.x, y: rect.y, width: rect.width, height: rect.height + }; + } + + function onMessageReceived(e) { + if (!e.data || !e.data.type) + return; + switch(e.data.type) { + case "scroll": + document.querySelector("button").scrollIntoView({behavior: "instant"}); + ackMessage({bounds: scrollingElementBounds()}, e.source); + break; + + case "scrolling-element-bounds": + ackMessage({bounds: scrollingElementBounds()}, e.source); + break; + } + } + + function ackMessage(msg, source) { + source.postMessage(msg, "*"); + } +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-action.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-action.html new file mode 100644 index 0000000000..51b715f30a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-action.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<style> + body, html { + height: 100%; + width: 100%; + overflow: hidden; + } + body { + touch-action: none; + } +</style> +<body> + <p>This page blocks all 'touch-action'.</p> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-block.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-block.html new file mode 100644 index 0000000000..4c204055af --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-block.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<style> +body, div, html { + height: 100%; + width: 100%; + overflow: hidden; +} +p { + margin-bottom: 1000px; + margin-right: 1000px; +} +</style> +<body> + <div id="blocker"> + <p>This page blocks 'touchstart' and 'touchmove'.</p> + </div> + <script> + function doSomethingUnimportant(e) { + return false !== e; + } + + function preventDefault(e) { + e.preventDefault(); + } + + document.addEventListener("touchstart", doSomethingUnimportant, {passive: false}); + document.addEventListener("touchmove", doSomethingUnimportant, {passive: false}); + + function messageHandler(e) { + let msg = e.data; + let element = document.querySelector(msg.query); + if (msg.prevent) { + element.addEventListener(msg.event_type, preventDefault, {passive: false}); + } else { + element.addEventListener(msg.event_type, doSomethingUnimportant); + } + e.source.postMessage(msg, "*"); + } + + window.addEventListener("message", messageHandler); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-wheel-block.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-wheel-block.html new file mode 100644 index 0000000000..21fc2b9b39 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-wheel-block.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<style> + body, html { + height: 100%; + width: 100%; + overflow: hidden; + } +</style> +<body> + <p>This page blocks all 'mouse-wheel'.</p> +<script> + function defaultScroll() { + window.scrollTo(0, 0); + } + + document.body.addEventListener( + "wheel", + (e) => { e.preventDefault(); defaultScroll(); }, {passive: false}); + + defaultScroll(); +</script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll.js b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll.js new file mode 100644 index 0000000000..88835cc602 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll.js @@ -0,0 +1,25 @@ +function rectMaxY(rect) { + return rect.height + rect.y; +} + +function rectMaxX(rect) { + return rect.width + rect.x; +} + +function isEmptyRect(rect) { + return !rect.width || !rect.height; +} + +// Returns true if the given rectangles intersect. +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); +} + +function rectToString(rect) { + return `Location: (${rect.x}, ${rect.y}) Size: (${rect.width}, ${rect.height})`; +} diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js b/testing/web-platform/tests/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js new file mode 100644 index 0000000000..3fdc9ed047 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js @@ -0,0 +1,22 @@ +// META: title='unload' Policy : allowed by default +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/unload-helper.js +// META: timeout=long + +'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 sameOriginSubframe = await main.addIframe(); + const crossOriginSubframe = await main.addIframe({ origin: 'HTTP_REMOTE_ORIGIN' }); + await assertWindowRunsUnload(sameOriginSubframe, 'sameOriginSubframe', { shouldRunUnload: true }); + await assertWindowRunsUnload(crossOriginSubframe, 'crossOriginSubframe', { shouldRunUnload: true }); + await assertWindowRunsUnload(main, 'main', {shouldRunUnload: true}); +}); diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js new file mode 100644 index 0000000000..b2fb19ae12 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js @@ -0,0 +1,21 @@ +// 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=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/unload-helper.js +// META: timeout=long + +'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}); +}); diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed.tentative.window.js b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed.tentative.window.js new file mode 100644 index 0000000000..c93443c101 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed.tentative.window.js @@ -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=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/unload-helper.js +// META: timeout=long + +'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}); +}); diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html new file mode 100644 index 0000000000..67cae05ee8 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html @@ -0,0 +1,113 @@ +<!doctype html> +<meta name="timeout" content="long"> +<title>vertical-scroll test for touch-action</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> + +<script> + "use strict"; + + let url = url_base + "vertical-scroll-scrollable-content.html"; + let iframeElement = document.querySelector("iframe"); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + // Sanity-check: Verify we can scroll using the test-API (empty <iframe>). + promise_test(async() => { + window.scrollTo(0, 0); + + await inject_input("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + await inject_input("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + }, "Verify that the page normally scrolls."); + + // Sanity-check: <iframe> normally scrolls. + promise_test(async() => { + // Make sure <window> can scroll both towards right and bottom. + window.scrollTo(0, 0); + + await loadUrlInIframe(iframeElement, url); + iframeElement.contentWindow.scrollTo(0, 0); + + await inject_input("down"); + assert_greater_than( + iframeElement.contentWindow.scrollY, + 0, + "<iframe> must have scrolled down."); + + + // Apply the scroll gesture. + await inject_input("right"); + assert_greater_than( + iframeElement.contentWindow.scrollX, + 0, + "<iframe> must have scrolled right."); + + }, "Verify that the <iframe> normally scrolls."); + + // Disable 'vertical-scroll': <iframe> should only scroll horizontally. + promise_test(async() => { + window.scrollTo(0, 0); + + // Disallow vertical scroll and reload the <iframe>. + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + iframeElement.contentWindow.scrollTo(0, 0); + + // Apply the scroll gesture. Main frame should and <iframe> should not + // scroll vertically. + await inject_input("down"); + assert_equals(iframeElement.contentWindow.scrollY, + 0, + "<iframe> must not scroll vertically."); + assert_greater_than(window.scrollY, + 0, + "Page must scroll vertically."); + + window.scrollTo(0, 0); + iframeElement.contentWindow.scrollTo(0, 0); + + await inject_input("right"); + assert_greater_than(iframeElement.contentWindow.scrollX, + 0, + "<iframe> must have scrolled right."); + }, "When 'vertical-scroll' is disabled in a document, scrollable contents " + + "can only *horizontally* scroll."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html new file mode 100644 index 0000000000..6522254076 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html @@ -0,0 +1,4 @@ +<!doctype html> +<title>vertical-scroll test for vertical scrollbar</title> +<link rel="match" href="/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html?no-vertical-scrollbar"> +<iframe src="/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html" allow="vertical-scroll 'none'"></iframe> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html new file mode 100644 index 0000000000..cda6c49abb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<title>Ensure 'vertical-scroll' does not affect main frame</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} +.spacer { + width: 100%; + height: 100%; + margin-top: 100%; + margin-bottom: 100%; +} +</style> +<p> Making sure there is room for vertical scroll </p> +<div class="spacer"></div> +<div class="spacer"></div> +</p>EOP</p> +<script> + "use strict"; + + // Sanity check. + test(() => { + assert_false(document.featurePolicy.allowsFeature("vertical-scroll"), + "Expected 'vertical-scroll' to be disabled."); + }, "'vertical-scroll' disabled in main document."); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + promise_test(async() => { + window.scrollTo(0, 0); + await inject_input("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + }, "Verify that the page scrolls vertically."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers new file mode 100644 index 0000000000..8d9c01e148 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers @@ -0,0 +1 @@ +Permissions-Policy: vertical-scroll=() diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html new file mode 100644 index 0000000000..4f78dd914a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html @@ -0,0 +1,117 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 95%; + height: 95%; + overflow: scroll; + margin-top: 200%; +} + +.spacer { + width: 100%; + height: 100%; + margin-top: 100%; + margin-bottom: 100%; +} + +</style> +<p> An <iframe> further below which is not allowed to block scroll.</p> +<div class="spacer"></div> +<iframe></iframe> +<p> Making sure there is room for vertical scroll </p> +<script> + "use strict"; + + let url = url_base + "vertical-scroll-scrollintoview.html"; + let iframeElement = document.querySelector("iframe"); + + function iframeBounds() { + return iframeElement.getBoundingClientRect(); + } + + // Enabled 'vertical-scroll': scrollIntoView should work in all frames. + promise_test(async() => { + window.scrollTo(0, 0); + await loadUrlInIframe(iframeElement, url); + + await sendMessageAndGetResponse( + iframeElement.contentWindow, + {type: "scrolling-element-bounds"}).then((response) => { + let iframeBoundsAtOrigin = { + x: 0, + y: 0, + width: iframeBounds().width, + height: iframeBounds().height}; + let scrollingElementBounds = response.bounds; + assert_false( + rects_intersect(iframeBoundsAtOrigin, scrollingElementBounds), + "Scrolling element should not be visible in <iframe>." + + `Scrolling element's bounds is: ${rectToString(response.bounds)} ` + + "but <iframe>'s size is:" + + `${iframeBounds().width}x${iframeBounds().height}.`); + }); + + // Scroll the scrolling element inside the <iframe>. + await sendMessageAndGetResponse(iframeElement.contentWindow, + {type: "scroll"}); + // The page should have scrolled vertically. + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + }, "Calling 'scrollIntoView()' inside a <iframe> will propagate up by" + + " default('vertical-scroll' enabled)."); + + // Disabled 'vertical-scroll': The scope of scrollIntoView is within the set + // of disabled frames (does not propagate to main frame). + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + + await sendMessageAndGetResponse( + iframeElement.contentWindow, + {type: "scrolling-element-bounds"}).then((response) => { + let iframeBoundsAtOrigin = { + x: 0, + y: 0, + width: iframeBounds().width, + height: iframeBounds().height}; + let scrollingElementBounds = response.bounds; + assert_false(rects_intersect(iframeBoundsAtOrigin, scrollingElementBounds), + "Scrolling element should not be visible in <iframe>." + + `Scrolling element's bounds is: ${rectToString(response.bounds)}` + + "but <iframe>'s size is:" + + `${iframeBounds().width}x${iframeBounds().height}.`); + }); + + // Scroll scrolling element inside the <iframe>. + await sendMessageAndGetResponse(iframeElement.contentWindow, + {type: "scroll"}).then((response) => { + // Make sure the nested <iframe> is visible. + let scrollingElementBounds = response.bounds; + let iframeBoundsAtOrigin = { + x: 0, + y: 0, + width: iframeBounds().width, + height: iframeBounds().height}; + // The scrolling element should be visible inside <iframe>. + assert_true(rects_intersect(iframeBoundsAtOrigin, scrollingElementBounds), + "Scrolling element should be visible in <iframe>." + + `Scrolling element's bounds is: ${rectToString(response.bounds)}` + + "but <iframe>'s size is:" + + `${iframeBounds().width}, ${iframeBounds().height}.`); + // The page however should not have scrolled. + assert_equals(window.scrollY, 0, "Main frame must not scroll vertically."); + }); + }, "Calling 'scrollIntoView()' inside a <iframe> with" + + " 'vertical-scroll none;'will not propagate upwards."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html new file mode 100644 index 0000000000..8c2eec93b7 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html @@ -0,0 +1,103 @@ +<!doctype html> +<meta name="timeout" content="long"> +<title>vertical-scroll test for touch-action</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> + +<script> + "use strict"; + + let url = url_base + "vertical-scroll-touch-action.html"; + let iframeElement = document.querySelector("iframe"); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + // Sanity-check: Verify we can scroll using the test-API (empty <iframe>). + promise_test(async() => { + window.scrollTo(0, 0); + + await inject_input("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + await inject_input("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + }, "Verify that the page normally scrolls."); + + // Enable 'vertical-scroll': "touch-action" should be able to block scroll. + promise_test(async() => { + // Make sure <window> can scroll both towards right and bottom. + window.scrollTo(0, 0); + + await loadUrlInIframe(iframeElement, url); + + // Apply the scroll gesture. + await inject_input("down"); + + // The scroll should normally bubble and affect this page, but the <iframe> + // is allowed to block it. + assert_equals(window.scrollY, + 0, + "Main frame must not scroll vertically"); + }, "Vertical scrolling through 'touch-action' can normally be blocked if" + + " 'pan-y' is not present."); + + // Disable 'vertical-scroll': "touch-action" should not be able to block + // scroll. + promise_test(async() => { + window.scrollTo(0, 0); + + // Disallow vertical scroll and reload the <iframe>. + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + + // Apply the scroll gesture. Main frame should scroll vertically. + await inject_input("down"); + + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + window.scrollTo(0, 0); + + await inject_input("right"); + assert_equals( + window.scrollX, + 0, + "'pan-x' can be blocked even when 'vertical-scroll' is disabled"); + }, "Vertical scrolling (and only vertical scrolling) through 'touch-action'" + + " cannot be blocked by a disabled frame."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html new file mode 100644 index 0000000000..341e543934 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html @@ -0,0 +1,237 @@ +<!doctype html> +<title>vertical-scroll test for touch-action</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> + +<script> + "use strict"; + + let url = url_base + "vertical-scroll-touch-block.html"; + let iframeElement = document.querySelector("iframe"); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + + }, "Make sure input injection API is ready."); + + // Sanity-check: Verify we can scroll using the test-API. The <iframe> with no + // query arguments would not preventDefault() any events. + promise_test(async() => { + window.scrollTo(0, 0); + await loadUrlInIframe(iframeElement, url); + + await inject_scroll("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + await inject_scroll("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + }, "Verify that the page normally scrolls."); + + // Enable 'vertical-scroll': Blocking "touchstart" can block scrolling. + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "*"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + + await inject_scroll("down"); + assert_equals(window.scrollY, + 0, + "Main frame must not scroll vertically."); + }, "Calling 'preventDefault' on 'touchstart' blocks vertical scroll when " + + " the feature is enabled."); + + // Disable 'vertical-scroll': Blocking "touchstart" cannot block vertical + // scroll. + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + + await inject_scroll("down"); + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + + // Horizontal scrolling must still be blocked. + window.scrollTo(0, 0); + await inject_scroll("right"); + assert_equals(window.scrollX, + 0, + "Main frame must not scroll horizontally."); + }, "When feature is disabled, calling 'preventDefault' on 'touchstart' does" + + " not block vertical scroll, but still bocks horizontal scroll."); + + // Enable 'vertical-scroll': Blocking "touchmove" can block scrolling. + promise_test(async() => { + // Make sure <window> can scroll both towards right and bottom. + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "*"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchmove", + query: "#blocker", + prevent: true + }); + + await inject_scroll("down"); + assert_equals(window.scrollY, + 0, + "Main frame must not scroll vertically"); + }, "Calling 'preventDefault' on 'touchmove' blocks vertical scroll when " + + "the feature is enabled."); + + // Disable 'vertical-scroll': Blocking "touchmove" cannot block vertical + // scroll. + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchmove", + query: "#blocker", + prevent: true + }); + await new Promise((r) => { + window.setTimeout(r, 100); + }); + + await inject_scroll("down"); + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + }, "When feature is disabled, calling 'preventDefault' on 'touchmove' does" + + " not block vertical scroll."); + + // Disable 'vertical-scroll': Add a non-preventing handler, and then a + // preventing handler and verify vertical scroll is possible and horizontal + // is blocked. + promise_test(async() => { + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + + // Add a non-blocking handler. + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: false + }); + + window.scrollTo(0, 0); + await inject_scroll("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + window.scrollTo(0, 0); + await inject_scroll("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + + // Add a blocking handler. + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + window.scrollTo(0, 0); + await inject_scroll("down"); + assert_greater_than(window.scrollY, + 0, + "Page must have scrolled down (preventing handler)."); + + window.scrollTo(0, 0); + await inject_scroll("right"); + assert_equals(window.scrollX, + 0, + "Page must not have scrolled horizontally."); + }, "Verify that starting with a non-preventing listener and then switching" + + " to a preventing one works as intended."); + + // Disable 'vertical-scroll': A preventing 'touchstart' handler does not allow + // other multi-touch actions such as 'pinch-zoom'. + promise_test(async() => { + assert_true(!!window.visualViewport, + "'visualViewport' is needed for this test."); + + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: false + }); + + // Sanity check: Pinch zoom in should work since 'touchstart' is not + // preventing. + let current_scale_factor = window.visualViewport.scale; + await inject_zoom("in"); + let new_scale_factor = window.visualViewport.scale; + assert_greater_than( + new_scale_factor, + current_scale_factor, + "Pinch zoom should work since 'touchstart' is not prevented."); + + // Add a preventing handler and verify pinch zoom is now blocked. + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + current_scale_factor = new_scale_factor; + await inject_zoom("out"); + new_scale_factor = window.visualViewport.scale; + assert_equals( + new_scale_factor, + current_scale_factor, + "Pinch zoom must be blocked."); + }, "Verify that pinch zoom is blocked in frames with 'vertical-scroll' none'" + + " where 'touchstart' is prevent defaulted."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html new file mode 100644 index 0000000000..2627fd4154 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html @@ -0,0 +1,145 @@ +<!doctype html> +<title>vertical-scroll test for 'mousewheel'</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} + +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> +<script> + "use strict"; + + let url = url_base + "vertical-scroll-wheel-block.html"; + function iframeElement() { + return document.querySelector("iframe"); + } + + // Used as the polling interval when waiting for a specific condition. + let verify_scroll_offset_delay = 5; + let no_scroll_timout = 50; + + function waitUntilSatisfied(testInstance, predicate) { + return new Promise((r) => { + function testPredicate() { + if (predicate()) { + r(); + } else { + testInstance.step_timeout(testPredicate, verify_scroll_offset_delay); + } + } + testPredicate(); + }); + } + + function resetScroll(testInstance) { + window.scrollTo({top: 0, left: 0, behavior: "instant"}); + return waitUntilSatisfied(testInstance, () => { + return window.scrollX === 0 && window.scrollY === 0; + }); + } + + function waitForMinimumScrollOffset(testInstance, minX, minY) { + return waitUntilSatisfied(testInstance, () => { + return window.scrollX >= minX && window.scrollY >= minY; + }); + } + + function waitFor(testInstance, delay) { + let checked_once = false; + return waitUntilSatisfied(testInstance, () => { + if (checked_once) + return true; + checked_once = true; + return false; + }); + } + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise((r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + // Sanity-check: Test API for scrolling along y-axis works as expected. + promise_test(async(testInstance) => { + await resetScroll(testInstance); + await inject_wheel_scroll("down"); + await waitForMinimumScrollOffset(testInstance, 0, 1); + assert_greater_than(window.scrollY, 0, "Expected vertical scroll."); + }, "Sanity-check: the page scrolls vertically in response to vertical wheel."); + + // Sanity-check: Test API for scrolling along x-axis works as expected. + promise_test(async(testInstance) => { + await resetScroll(testInstance); + await inject_wheel_scroll("right"); + await waitForMinimumScrollOffset(testInstance, 1, 0); + assert_greater_than(window.scrollX, 0, "Expected horizontal scroll."); + }, "Sanity-check: the page scrolls horizontally in response to horizontal wheel."); + + // Test that when 'vertical-scroll' is enabled, vertical scroll can be + // blocked by canceling 'wheel' event. + promise_test(async(testInstance) => { + setFeatureState(iframeElement(), "vertical-scroll", "*"); + await loadUrlInIframe(iframeElement(), url); + + await resetScroll(testInstance); + await inject_wheel_scroll("down") + await waitFor(testInstance, no_scroll_timout); + assert_equals(window.scrollY, 0, "Did not expected vertical scroll."); + }, "When 'vertical-scroll' is enabled canceling vertical 'wheel' " + + "blocks vertical scrolling."); + + // Test that when 'vertical-scroll' is disabled, vertical scroll cannot + // be blocked by canceling 'wheel' event. + promise_test(async(testInstance) => { + setFeatureState(iframeElement(), "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement(), url); + + await resetScroll(testInstance); + await inject_wheel_scroll("down"); + await waitForMinimumScrollOffset(testInstance, 0, 1); + assert_greater_than(window.scrollY, 0, "Expected vertical scroll."); + }, "When 'vertical-scroll' is disabled canceling vertical 'wheel' " + + "does not block vertical scrolling."); + + // Test that when 'vertical-scroll' is disabled, horizontal scroll can be + // blocked by canceling 'wheel' event. + promise_test(async(testInstance) => { + setFeatureState(iframeElement(), "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement(), url); + + await resetScroll(testInstance); + await inject_wheel_scroll("right"); + await waitFor(testInstance, no_scroll_timout); + assert_equals(window.scrollX, 0, "Did not expect horizontal scroll."); + }, "When 'vertical-scroll' is disabled canceling horizontal 'wheel' " + + "blocks horizontal scrolling."); +</script> |