diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/permissions | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
201 files changed, 6260 insertions, 0 deletions
diff --git a/testing/web-platform/tests/permissions-policy/META.yml b/testing/web-platform/tests/permissions-policy/META.yml new file mode 100644 index 0000000000..7b2e2cbffe --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/META.yml @@ -0,0 +1,3 @@ +spec: https://w3c.github.io/webappsec-permissions-policy/ + - bakulf + - clelland diff --git a/testing/web-platform/tests/permissions-policy/README.md b/testing/web-platform/tests/permissions-policy/README.md new file mode 100644 index 0000000000..ae17c099e5 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/README.md @@ -0,0 +1,59 @@ +# Permissions Policy Guide +## How to Test a New Feature with permissions policy + +This directory contains a framework to test features with permissions policy. + +When adding a new feature to permissions policy, the following cases should be tested: +* feature enabled by header policy [HTTP tests] + + test when feature is enabled by permissions policy HTTP header; +* feature disabled by header policy [HTTP tests] + + test when feature is disabled by permissions policy HTTP header; +* feature enabled on self origin by header policy [HTTP tests] + + test when feature is enabled only on self origin by permissions policy HTTP + header. +* feature allowed by container policy (iframe "allow" attribute); + + test when feature is enabled by iframe "allow" attribute on self and cross + origins. +* feature allowed by container policy, redirect on load. + + test when feature is enabled by iframe "allow" attribute when the iframe + is being redirected to a new origin upon loading + +### How to Use the Test Framework +Use `test_feature_availability()` defined in +`/permissions-policy/resources/permissions-policy.js`. Please refer to the comments +in `/permissions-policy/resources/permissions-policy.js` for how this function works. + +### How to Write Header Policy Tests +HTTP tests are used to test features with header policy. + +* Define the header policy in `<feature-name>-<enabled | disabled | enabled-on-self-origin>-by-permissions-policy.https.sub.html.headers`. Example: + + Permissions-Policy: feature-name=* + + +* In `<feature-name>-<enabled | disabled | enabled-on-self-origin>-by-permissions-policy.https.sub.html`: +* test if feature is enabled / disabled in the main frame; +* test if feature is enabled / disabled in a same-origin iframe; +* test if feature is enabled / disabled in a cross-origin iframe. + +Examples: +`/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html` +`/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html.headers` + +### How to Write Container Policy Tests +Simply use `test_feature_availability()` with the optional argument +`feature_name` specified to test if: +* feature is enabled / disabled in a same-origin iframe; +* feature is enabled / disabled in a cross-origin iframe. + +Example: +`/permissions-policy/payment-allowed-by-permissions-policy-attribute.https.sub.html` + +### How to Write Container Policy Tests with Redirect +Similar to the section above, append +`/permissions-policy/resources/redirect-on-load.html#` to the argument `src` +passed to `test_feature_availability()`. + +Example: +`/permissions-policy/payment-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html` + diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html new file mode 100644 index 0000000000..a5f4479511 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/bluetooth/resources/bluetooth-test.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict' + + const relative_path = '/permissions-policy/resources/permissions-policy-bluetooth.html'; + const base_src = '/permissions-policy/resources/redirect-on-load.html#'; + const same_origin_src = base_src + relative_path; + const cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' + relative_path; + const feature_name = 'permissions policy "bluetooth"'; + const header = 'permissions policy allow="bluetooth"'; + + bluetooth_test(() => { + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, same_origin_src, expect_feature_available_default, 'bluetooth'); + }, header + ' allows same-origin navigation in an iframe.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_unavailable_default, 'bluetooth'); + }, header + ' disallows cross-origin navigation in an iframe.'); + }); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy-attribute.https.sub.html b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy-attribute.https.sub.html new file mode 100644 index 0000000000..5b1f69e86c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy-attribute.https.sub.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/bluetooth/resources/bluetooth-test.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict' + + const same_origin_src = '/permissions-policy/resources/permissions-policy-bluetooth.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; + const feature_name = 'permissions policy "bluetooth"'; + const header = 'allow="bluetooth" attribute'; + + bluetooth_test(() => { + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, same_origin_src, expect_feature_available_default, 'bluetooth'); + }, feature_name + ' can be enabled in same-origin iframe using ' + header); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_available_default, 'bluetooth'); + }, feature_name + ' can be enabled in cross-origin iframe using ' + header); + }); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..283c613271 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy.https.sub.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/bluetooth/resources/bluetooth-test.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-bluetooth.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; + const header = 'permissions policy header "bluetooth=*"'; + + bluetooth_test(() => { + promise_test(() => { + return navigator.bluetooth.getDevices(); + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, same_origin_src, expect_feature_available_default); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_unavailable_default); + }, header + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_available_default, 'bluetooth'); + }, header + ' allow="bluetooth" allows cross-origin iframes.'); + }); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..a053d1a213 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-allowed-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: bluetooth=*
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-default-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/bluetooth-default-permissions-policy.https.sub.html new file mode 100644 index 0000000000..51ab40bca9 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-default-permissions-policy.https.sub.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/bluetooth/resources/bluetooth-test.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-bluetooth.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; + const header = 'Default "bluetooth" permissions policy'; + + bluetooth_test(() => { + promise_test(() => { + return navigator.bluetooth.getDevices(); + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, same_origin_src, expect_feature_available_default); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_unavailable_default); + }, header + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_available_default, 'bluetooth'); + }, header + ' allow="bluetooth" allows cross-origin iframes.'); + }); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-disabled-by-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/bluetooth-disabled-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..7a20a9f01f --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-disabled-by-permissions-policy.https.sub.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/bluetooth/resources/bluetooth-test.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-bluetooth.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; + const header = 'permissions policy header "bluetooth=()"'; + + bluetooth_test(() => { + promise_test(() => { + return navigator.bluetooth.getDevices().then(() => { + assert_unreached('expected promise to reject with SecurityError.'); + }, error => { + assert_equals(error.name, 'SecurityError'); + }); + }, header + ' disallows the top-level document.') + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, same_origin_src, expect_feature_unavailable_default); + }, header + ' disallows same-origin iframes.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_unavailable_default); + }, header + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability('bluetooth.getDevices()', t, cross_origin_src, expect_feature_unavailable_default, 'bluetooth'); + }, header + ' allow="bluetooth" has no effect on cross-origin iframes.'); + }); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/bluetooth-disabled-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/bluetooth-disabled-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..10b94729dd --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/bluetooth-disabled-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: bluetooth=()
\ No newline at end of file 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/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-trust-token-redemption.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-trust-token-redemption.html new file mode 100644 index 0000000000..db24b04824 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-trust-token-redemption.html @@ -0,0 +1,56 @@ +<script> + 'use strict'; + + window.onload = function() { + // When the trust-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/", { + trustToken: { + type: "token-redemption" + } + }); + } catch (e) { + num_enabled--; + } + try { + new Request("https://destination.example/", { + trustToken: { + type: "send-redemption-record", + issuers: ["https://issuer.example/"] + } + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setTrustToken({ + type: "token-redemption" + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://destination.example/"); + xhr.setTrustToken({ + type: "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/trust-token-redemption-default-permissions-policy.tentative.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/trust-token-redemption-default-permissions-policy.tentative.https.sub.html new file mode 100644 index 0000000000..2073687910 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/trust-token-redemption-default-permissions-policy.tentative.https.sub.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<title>Test that trust 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-trust-token-redemption.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Default "trust-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/", { + trustToken: { + type: "token-redemption" + } + }); + new Request("https://destination.example/", { + trustToken: { + type: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + } + }); + + const redemption_xhr = new XMLHttpRequest(); + redemption_xhr.open("GET", "https://issuer.example/"); + redemption_xhr.setTrustToken({ + type: "token-redemption" + }); + + const signing_xhr = new XMLHttpRequest(); + signing_xhr.open("GET", "https://destination.example/"); + signing_xhr.setTrustToken({ + type: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + }); + } catch (e) { + assert_unreached(); + } + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('Trust 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('Trust 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/trust-token-redemption-supported-by-permissions-policy.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/trust-token-redemption-supported-by-permissions-policy.tentative.html new file mode 100644 index 0000000000..e349eadc5d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/trust-token-redemption-supported-by-permissions-policy.tentative.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<title>Test that trust 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('trust-token-redemption', document.featurePolicy.features()); + }, 'document.featurePolicy.features should advertise trust token redemption.'); +</script> 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> diff --git a/testing/web-platform/tests/permissions-policy/idlharness.window.js b/testing/web-platform/tests/permissions-policy/idlharness.window.js new file mode 100644 index 0000000000..90dbb4961a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/idlharness.window.js @@ -0,0 +1,20 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +"use strict"; + +// https://wicg.github.io/permissions-policy/ + +idl_test( + ['permissions-policy'], + ['reporting', 'html', 'dom'], + idl_array => { + idl_array.add_objects({ + Document: ['document'], + HTMLIFrameElement: ['document.createElement("iframe")'], + PermissionsPolicy: ['document.permissionsPolicy'], + // TODO: PermissionsPolicyViolationReportBody + }); + } +); diff --git a/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html new file mode 100644 index 0000000000..e43f4c80ce --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<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'; + var relative_path = '/permissions-policy/resources/permissions-policy-payment.html'; + var base_src = '/permissions-policy/resources/redirect-on-load.html#'; + var same_origin_src = base_src + relative_path; + var cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' + + relative_path; + var header = 'permissions policy allow="payment"'; + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, same_origin_src, + expect_feature_available_default, 'payment'); + }, header + ' allows same-origin navigation in an iframe.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, cross_origin_src, + expect_feature_unavailable_default, 'payment'); + }, header + ' disallows cross-origin navigation in an iframe.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, same_origin_src, + expect_feature_available_default, 'payment', 'allowpaymentrequest'); + }, header + ' allowpaymentrequest=true allows same-origin navigation in an iframe.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, cross_origin_src, + expect_feature_unavailable_default, 'payment', 'allowpaymentrequest'); + }, header + ' allowpaymentrequest=true disallows cross-origin navigation in an iframe.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute.https.sub.html new file mode 100644 index 0000000000..779e9d666c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute.https.sub.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<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'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var feature_name = 'permissions policy "payment"'; + var header = 'allow="payment" attribute'; + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, same_origin_src, + expect_feature_available_default, 'payment'); + }, feature_name + ' can be enabled in same-origin iframe using ' + header); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, cross_origin_src, + expect_feature_available_default, 'payment'); + }, feature_name + ' can be enabled in cross-origin iframe using ' + header); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..456626c350 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy.https.sub.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<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'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var header = 'permissions policy header "payment=*"'; + + test(() => { + var supportedMethods = [ { supportedMethods: 'https://{{domains[nonexistent]}}/payment-request' } ]; + var details = { + total: { label: 'Test', amount: { currency: 'USD', value: '5.00' } } + }; + try { + new PaymentRequest(supportedMethods, details); + } catch (e) { + assert_unreached(); + } + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, same_origin_src, + expect_feature_available_default); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, cross_origin_src, + expect_feature_unavailable_default); + }, header + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, cross_origin_src, + expect_feature_available_default, 'payment'); + }, header + ' allow="payment" allows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..73449d512d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: payment=* diff --git a/testing/web-platform/tests/permissions-policy/payment-default-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-default-permissions-policy.https.sub.html new file mode 100644 index 0000000000..71ab15ae89 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-default-permissions-policy.https.sub.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<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'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var header = 'Default "payment" permissions policy'; + + test(() => { + var supportedInstruments = [ { supportedMethods: 'visa' } ]; + var details = { + total: { label: 'Test', amount: { currency: 'USD', value: '5.00' } } + }; + try { + new PaymentRequest(supportedInstruments, details); + } catch (e) { + assert_unreached(); + } + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, same_origin_src, + expect_feature_available_default); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, cross_origin_src, + expect_feature_unavailable_default); + }, header + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, same_origin_src, + expect_feature_available_default, undefined, 'allowpaymentrequest'); + }, header + ' allowpaymentrequest=true allows same-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, cross_origin_src, + expect_feature_available_default, undefined, 'allowpaymentrequest'); + }, header + ' allowpaymentrequest=true allows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..90f3a0ee03 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<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'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var header = 'permissions policy header "payment=()"'; + + test(() => { + var supportedInstruments = [ { supportedMethods: 'visa' } ]; + var details = { + total: { label: 'Test', amount: { currency: 'USD', value: '5.00' } } + }; + assert_throws_dom('SecurityError', () => { + new PaymentRequest(supportedInstruments, details); + }); + }, header + ' disallows the top-level document.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, same_origin_src, + expect_feature_unavailable_default); + }, header + ' disallows same-origin iframes.'); + + async_test(t => { + test_feature_availability('PaymentRequest()', t, cross_origin_src, + expect_feature_unavailable_default,); + }, header + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, same_origin_src, + expect_feature_unavailable_default, undefined, 'allowpaymentrequest'); + }, header + ' allowpaymentrequest=true disallows same-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'PaymentRequest()', t, cross_origin_src, + expect_feature_unavailable_default, undefined, 'allowpaymentrequest'); + }, header + ' allowpaymentrequest=true disallows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..49f799d138 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: payment=() diff --git a/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html new file mode 100644 index 0000000000..de55531b6c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<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'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-payment-extension.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var feature_name = 'permissions policy "payment"'; + var header = 'allow="payment" attribute'; + + async_test(t => { + test_feature_availability_with_post_message_result( + t, cross_origin_src, "NotSupportedError#The 'payment' feature is not " + + "enabled in this document. Permissions Policy may be used to " + + "delegate Web Payment capabilities to cross-origin child frames."); + }, feature_name + ' is not supported in cross-origin iframe without ' + header); + + async_test(t => { + test_feature_availability_with_post_message_result( + t, cross_origin_src, 'OK', 'payment'); + }, feature_name + ' can be enabled in cross-origin iframe using ' + header); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/payment-supported-by-permissions-policy.tentative.html b/testing/web-platform/tests/permissions-policy/payment-supported-by-permissions-policy.tentative.html new file mode 100644 index 0000000000..3b9928fe19 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-supported-by-permissions-policy.tentative.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<title>Test that payment is advertised in the feature list</title> +<link rel="help" href="https://w3c.github.io/webappsec-permissions-policy/#dom-permissionspolicy-features"> +<link rel="help" href="https://github.com/w3c/payment-request/issues/600"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(() => { + assert_in_array('payment', document.permissionsPolicy.features()); +}, 'document.featurePolicy.features should advertise payment.'); +</script> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-all.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-all.https.sub.html new file mode 100644 index 0000000000..329b6a9ea9 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-all.https.sub.html @@ -0,0 +1,150 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> +</head> +<body> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=*; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var data_src = 'data:text/html,<h1>data: URL</h1>'; + var policies = [ + {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: true}, + {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: false}]; + var pipe_front = '?pipe=sub|header(Permissions-Policy,fullscreen='; + var pipe_end = ';)'; + var header_policies = ["*", "self", "()"]; + + // Test that frame.policy inherits from parent's header policy when allow + // attribute is not specified. + test(function() { + test_frame_policy('fullscreen', same_origin_src, undefined, true); + }, 'Test frame policy on same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, undefined, true); + }, 'Test frame policy on cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', undefined, true, true); + }, 'Test frame policy on srcdoc iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', same_origin_src, true, true); + }, 'Test frame policy on srcdoc+ same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, true, true); + }, 'Test frame policy on srcdoc+ cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', data_src, undefined, true); + }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j].replace(")", "\\)") + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j].replace(")", "\\)") + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + } + } + + // Test that the allow attribute overrides allowfullscreen. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..2cbb8a82c6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=* diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..1aa219bffe --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-self.https.sub.html @@ -0,0 +1,184 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> +</head> +<body> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=self; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var data_src = 'data:text/html,<h1>data: URL</h1>'; + var policies = [ + {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: false}, + {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: false}]; + var pipe_front = '?pipe=sub|header(Permissions-Policy, fullscreen='; + var pipe_end = ';)'; + var header_policies = ["*", "self", "()"]; + + // Test that frame.policy inherits from parent's header policy when allow + // attribute is not specified. + test(function() { + test_frame_policy('fullscreen', same_origin_src, undefined, true); + }, 'Test frame policy on same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, undefined, false); + }, 'Test frame policy on cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', undefined, true, true); + }, 'Test frame policy on srcdoc iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', same_origin_src, true, true); + }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, true, true); + }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', data_src, undefined, false); + }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.'); + + // Test that frame policy can be used for sandboxed frames + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, false, undefined, false, true); + }, 'Test frame policy on sandboxed iframe with no allow attribute.'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, true, 'fullscreen', false, true); + }, 'Test frame policy on sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, true, 'fullscreen \'src\'', false, true); + }, 'Test frame policy on sandboxed iframe with allow="fullscreen \'src\'".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, false, 'fullscreen ' + cross_origin, false, true); + }, 'Test frame policy on sandboxed iframe with allow="fullscreen ' + cross_origin + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, true, 'fullscreen', false, true); + }, 'Test frame policy on srcdoc sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, true, 'fullscreen', false, true); + }, 'Test frame policy on srcdoc + same origin sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, true, 'fullscreen', false, true); + }, 'Test frame policy on srcdoc + cross origin sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, false, 'fullscreen ' + cross_origin, false, true); + }, 'Test frame policy on sandboxed srcdoc iframe with allow="fullscreen ' + cross_origin + '".'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + } + } + + // Test that the allow attribute overrides allowfullscreen. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on data: URL origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-self.https.sub.html.sub.headers new file mode 100644 index 0000000000..ff7ae41353 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-self.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=self diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some-override.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some-override.https.sub.html new file mode 100644 index 0000000000..250564440f --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some-override.https.sub.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> +</head> +<body> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=self cross_origin https://www.example.com; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var data_src = 'data:text/html,<h1>data: URL</h1>'; + // Test permissions policy with same_origin_src and cross_origin_src. + var policies = [ + {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: true, dataOriginTestExpect: false}, + {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, crossOrigin1TestExpect: false, dataOriginTestExpect: false}, + {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, crossOrigin1TestExpect: false, dataOriginTestExpect: false}, + {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: false, dataOriginTestExpect: false}]; + + // Test that the allow attribute overrides allowfullscreen. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src1, undefined, + policies[i].crossOrigin1TestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src1, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + another cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some-override.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some-override.https.sub.html.sub.headers new file mode 100644 index 0000000000..d3aa9ff66a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some-override.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=self https://{{domains[www]}}:{{ports[https][0]}} https://www.example.com diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some.https.sub.html new file mode 100644 index 0000000000..a27018e98f --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some.https.sub.html @@ -0,0 +1,136 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> +</head> +<body> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=self cross_origin https://www.example.com; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var data_src = 'data:text/html,<h1>data: URL</h1>'; + // Test permissions policy with same_origin_src and cross_origin_src. + var policies = [ + {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: true, dataOriginTestExpect: false}, + {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, crossOrigin1TestExpect: false, dataOriginTestExpect: false}, + {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, crossOrigin1TestExpect: false, dataOriginTestExpect: false}, + {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: false, dataOriginTestExpect: false}]; + var pipe_front = '?pipe=sub|header(Permissions-Policy, fullscreen='; + var pipe_end = ')'; + var header_policies = ["*", "self", "()"]; + + // Test that frame.policy inherits from parent's header policy when allow + // attribute is not specified. + test(function() { + test_frame_policy('fullscreen', same_origin_src, undefined, true); + }, 'Test frame policy on same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, undefined, true); + }, 'Test frame policy on cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src1, undefined, false); + }, 'Test frame policy on another cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', undefined, true, true); + }, 'Test frame policy on srcdoc iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', same_origin_src, true, true); + }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, true, true); + }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src1, true, true); + }, 'Test frame policy on srcdoc + another cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', data_src, undefined, false); + }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src1, undefined, + policies[i].crossOrigin1TestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src1, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + another cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src1 + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOrigin1TestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + } + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some.https.sub.html.sub.headers new file mode 100644 index 0000000000..af08d49c2e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-allowed-for-some.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=(self "https://{{domains[www]}}:{{ports[https][0]}}" "https://www.example.com") diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-disallowed-for-all.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-disallowed-for-all.https.sub.html new file mode 100644 index 0000000000..e7869b1ea1 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-disallowed-for-all.https.sub.html @@ -0,0 +1,150 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> +</head> +<body> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=(); --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var data_src = 'data:text/html,<h1>data: URL</h1>'; + var policies = [ + {allow: "*", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'self'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}, + {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}]; + var pipe_front = '?pipe=sub|header(Permissions-Policy, fullscreen='; + var pipe_end = ';)'; + var header_policies = ["*", "self", "()"]; + + // Test that frame.policy inherits from parent's header policy when allow + // attribute is not specified. + test(function() { + test_frame_policy('fullscreen', same_origin_src, undefined, false); + }, 'Test frame policy on same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, undefined, false); + }, 'Test frame policy on cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', undefined, true, false); + }, 'Test frame policy on srcdoc iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', same_origin_src, true, false); + }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, true, false); + }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', data_src, undefined, false); + }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Permissions-Policy: fullscreen=' + header_policies[j] + ';".'); + } + } + + // Test that the allow attribute overrides allowfullscreen. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + + '" and allowfullscreen.'); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-disallowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-disallowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..a65abd6e45 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-disallowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=() diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-timing-iframe-camera.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-timing-iframe-camera.https.sub.html new file mode 100644 index 0000000000..995ac2134e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-timing-iframe-camera.https.sub.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script> +window.onmessage = m => { + navigator.mediaDevices.getUserMedia({video: true}) + .then(() => 'ok') + .catch(e => e.name) + .then(r => { + window.parent.postMessage({ + id: m.data.id, + result: r + }, '*'); + }); +} +</script> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-timing.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-timing.https.sub.html new file mode 100644 index 0000000000..5b2a488863 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-frame-policy-timing.https.sub.html @@ -0,0 +1,69 @@ +<!doctype html> +<html> + <header> + <title>allow/sandbox attr changed after document creation, before response</title> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + let lastCallbackId = 0; + const callbacks = {}; + + function postMessageToFrame(frame, cb) { + var id = ++lastCallbackId; + callbacks[id] = cb; + frame.contentWindow.postMessage({id:id}, '*'); + step_timeout(() => { + if (id in callbacks) { + callbacks[id]('timeout'); + delete callbacks[id]; + } + }, 1000); + } + + window.onmessage = function(e) { + const message = e.data; + const id = message['id']; + const callback = callbacks[id]; + delete callbacks[id]; + callback(message.result); + }; + // @param {string} url + // @param {Function} iframe_pre_nav_callback - a callback with signature (iframe) => () which gets + // triggered before setting src attribute. + // @param {Function} iframe_post_nav_callback - a callback with signature (iframe) => () which gets + // triggered after setting src attribute but before commit + // of navigation. + // @param {Function} result_handler - a callback that handles the result posted back from iframe. + // @param {string} test_name + function timing_test(url, + iframe_pre_nav_callback, + iframe_post_nav_callback, + result_handler, test_name) { + async_test((t) => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + iframe_pre_nav_callback(iframe); + iframe.src = url; + iframe_post_nav_callback(iframe); + iframe.onload = t.step_func(() => { + postMessageToFrame(iframe, t.step_func_done(result_handler)); + }); + }, test_name); + } + + const path = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1); + const same_origin = path; + const cross_origin = "https://{{domains[www1]}}:{{ports[https][0]}}" + path; + + const cameraUrl = 'permissions-policy-frame-policy-timing-iframe-camera.https.sub.html'; + function disallowCamera(iframe) { iframe.allow = "camera 'none'"; } + function allowCamera(iframe) { iframe.allow = 'camera *'; } + function verifyCamera(result) { assert_equals(result, 'NotAllowedError'); } + timing_test(same_origin + cameraUrl, disallowCamera, allowCamera, verifyCamera, 'allow attr timing test same origin'); + timing_test(cross_origin + cameraUrl, disallowCamera, allowCamera, verifyCamera, 'allow attr timing test diff origin'); + </script> + </header> + <body> + <div id="log"></div> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-all.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-all.https.sub.html new file mode 100644 index 0000000000..16a2b60c75 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-all.https.sub.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=*; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var header_policy = 'Permissions-Policy: fullscreen=*'; + + // Test that fullscreen's allowlist is ['*'] + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + ['*']); + }, header_policy + ' -- test allowlist is ['*']'); + + // Test that fullscreen is allowed on same-origin subframes. + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src); + + // Test that fullscreen is not allowed on cross-origin subframes without an + // allow attribute. + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src); + + // Dynamically update sub frame's container policy to self + var allow = "fullscreen 'self';" + test_allowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + + // Dynamically update sub frame's container policy to src + var allow = "fullscreen 'src';" + test_allowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_allowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is allowed on cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..2cbb8a82c6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=* diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-malformed-wildcard.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-malformed-wildcard.https.sub.html new file mode 100644 index 0000000000..daa47efb67 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-malformed-wildcard.https.sub.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=$MALFORMED_WILDCARD_ORIGINS self; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var header_policy = `Permissions-Policy: fullscreen=("$MALFORMED_WILDCARD_ORIGINS self")`; + + // Test that fullscreen's allowlist lists all the malformed wildcards and self. + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen').sort(), + ["https://%2A.%2A.{{domains[]}}:{{ports[https][0]}}", "https://example.%2A.{{domains[]}}:{{ports[https][0]}}", "https://{{domains[]}}:{{ports[https][0]}}"].sort()); + }, header_policy + ' -- test allowlist lists all the malformed wildcards and self.'); + + // Test that fullscreen is allowed on same-origin subframes with or without an allow attribute. + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src); + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe with allow attribute', + 'fullscreen', + same_origin_src, + "fullscreen " + same_origin); + + // Test that fullscreen is disallowed on cross-origin subframes without an allow attribute. + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe allow attribute', + 'fullscreen', + cross_origin_src, + "fullscreen " + cross_origin); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on another cross-origin subframe', + 'fullscreen', + cross_origin_src1); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on another cross-origin subframe allow attribute', + 'fullscreen', + cross_origin_src1, + "fullscreen " + cross_origin1); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-malformed-wildcard.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-malformed-wildcard.https.sub.html.sub.headers new file mode 100644 index 0000000000..cfb0f3f5ae --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-malformed-wildcard.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("*://{{domains[]}}:{{ports[https][0]}}" "https://{{domains[]}}:*" "https://*.*.{{domains[]}}:{{ports[https][0]}}" "https://example.*.{{domains[]}}:{{ports[https][0]}}" self) diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..363ff0b981 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=self; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var header_policy = 'Permissions-Policy: fullscreen=self'; + + // Test that fullscreen's allowlist is ['same_origin'] + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [same_origin]); + }, header_policy + ' -- test allowlist is [same_origin]'); + + // Test that fullscreen is only allowed on same-origin subframe. + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src); + + // Dynamically update sub frame's container policy + var allow = "fullscreen 'src';" + test_allowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html.sub.headers new file mode 100644 index 0000000000..ff7ae41353 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=self diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-some.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-some.https.sub.html new file mode 100644 index 0000000000..e9d8ac1458 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-some.https.sub.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=self cross_origin https://www.example.com; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var header_policy = `Permissions-Policy: fullscreen=(self "${cross_origin}" "https://www.example.com")`; + + // Test that fullscreen's allowlist is [same_origin, cross_origin, 'https://www.example.com'] + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen').sort(), + [same_origin, cross_origin, 'https://www.example.com'].sort()); + }, header_policy + ' -- test allowlist is [same_origin, cross_origin, https://www.example.com]'); + + // Test that fullscreen is allowed on same-origin, but disallowd on cross- + // origin subframes. without an allow attribute. + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin ' + cross_origin_src + ' subframe', + 'fullscreen', + cross_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin ' + cross_origin_src1 + ' subframe', + 'fullscreen', + cross_origin_src1); + + // dynamically update sub frame's container policy to none + var allow = "fullscreen 'none';" + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + allow + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + allow + ' -- test fullscreen is disallowed on another cross-origin subframe', + 'fullscreen', + cross_origin_src1, + allow); + + // dynamically update sub frame's container policy to src + allow = "fullscreen 'src';" + test_allowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_allowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + allow + ' -- test fullscreen is allowed on cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on another cross-origin subframe', + 'fullscreen', + cross_origin_src1, + allow); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-some.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-some.https.sub.html.sub.headers new file mode 100644 index 0000000000..af08d49c2e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-some.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=(self "https://{{domains[www]}}:{{ports[https][0]}}" "https://www.example.com") diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-wildcard.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-wildcard.https.sub.html new file mode 100644 index 0000000000..713d697e64 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-wildcard.https.sub.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=self wildcard_origin; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var wildcard_origin = 'https://*.{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var header_policy = `Permissions-Policy: fullscreen=("${wildcard_origin}")`; + + // Test that fullscreen's allowlist is [self wildcard_origin]. + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen').sort(), + [wildcard_origin, same_origin].sort()); + }, header_policy + ' -- test allowlist is [self wildcard_origin].'); + + // Test that fullscreen is allowed on same-origin subframes with or without an allow attribute. + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe', + 'fullscreen', + same_origin_src); + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on same-origin subframe even with allow attribute', + 'fullscreen', + same_origin_src, + "fullscreen " + same_origin); + + // Test that fullscreen is disallowed on cross-origin subframes without an allow attribute. + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src); + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on cross-origin subframe allow attribute', + 'fullscreen', + cross_origin_src, + "fullscreen " + cross_origin); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on another cross-origin subframe', + 'fullscreen', + cross_origin_src1); + test_allowed_feature_for_subframe( + header_policy + ' -- test fullscreen is allowed on another cross-origin subframe allow attribute', + 'fullscreen', + cross_origin_src1, + "fullscreen " + cross_origin1); + + // Test that wildcard allow attribute isn't supported. + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe with wildcard allow attribute', + 'fullscreen', + cross_origin_src, + "fullscreen " + wildcard_origin); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on another cross-origin subframe with wildcard allow attribute', + 'fullscreen', + cross_origin_src1, + "fullscreen " + wildcard_origin); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-wildcard.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-wildcard.https.sub.html.sub.headers new file mode 100644 index 0000000000..cc492ecc92 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-allowed-for-wildcard.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=(self "https://*.{{domains[]}}:{{ports[https][0]}}") diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-declined.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-declined.https.sub.html new file mode 100644 index 0000000000..f02cdce7d1 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-declined.https.sub.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=cross_origin https://www.example.com; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var header_policy = `Permissions-Policy: fullscreen=(self "${cross_origin}" "https://www.example.com"`; + + // Test that fullscreen's allowlist is [same_origin, cross_origin, 'https://www.example.com'] + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [cross_origin, 'https://www.example.com'].sort()); + }, header_policy + ' -- test allowlist is [cross_origin, https://www.example.com]'); + + // Test that fullscreen is everywhere. + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on same-origin subframe', + 'fullscreen', + same_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin ' + cross_origin_src + ' subframe', + 'fullscreen', + cross_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin ' + cross_origin_src1 + ' subframe', + 'fullscreen', + cross_origin_src1); + + // dynamically update sub frame's container policy to none + var disallow = "fullscreen 'none';" + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + disallow + ' -- test fullscreen is disallowed on same-origin subframe', + 'fullscreen', + same_origin_src, + disallow); + test_disallowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + disallow + ' -- test fullscreen is disallowed on specific cross-origin subframe', + 'fullscreen', + cross_origin_src, + disallow); + test_disallowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + disallow + ' -- test fullscreen is disallowed on another cross-origin subframe', + 'fullscreen', + cross_origin_src1, + disallow); + + // dynamically update sub frame's container policy to cross_origin + var allow = "fullscreen " + cross_origin; + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + allow + ' -- test fullscreen is disallowed on specific cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + 'iframe.allow = ' + allow + ' -- test fullscreen is disallowed on another cross-origin subframe', + 'fullscreen', + cross_origin_src1, + allow); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-declined.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-declined.https.sub.html.sub.headers new file mode 100644 index 0000000000..175e5465d2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-declined.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https://{{domains[www]}}:{{ports[https][0]}}" "https://www.example.com") diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-disallowed-for-all.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-disallowed-for-all.https.sub.html new file mode 100644 index 0000000000..f787b7de12 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-disallowed-for-all.https.sub.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <!-- Permissions-Policy: fullscreen=(); --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var header_policy = 'Permissions-Policy: fullscreen=()'; + + // Test that fullscreen's allowlist is [] + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + []); + assert_false(document.fullscreenEnabled, "fullscreenEnabled should reflect permissions policy properly"); + }, header_policy + ' -- test allowlist is []'); + + // Test that fullscreen is disallowed on all subframes. + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on same-origin subframe', + 'fullscreen', + same_origin_src); + test_disallowed_feature_for_subframe( + header_policy + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src); + + // Dynamically update sub frame's container policy + var allow = "fullscreen 'src';" + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on same-origin subframe', + 'fullscreen', + same_origin_src, + allow); + test_disallowed_feature_for_subframe( + header_policy + ', iframe.allow = ' + allow + ' -- test fullscreen is disallowed on cross-origin subframe', + 'fullscreen', + cross_origin_src, + allow); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..bbc80c116a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers @@ -0,0 +1,2 @@ +Permissions-Policy: fullscreen=() + diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-javascript-url-frame-policy.https.html b/testing/web-platform/tests/permissions-policy/permissions-policy-javascript-url-frame-policy.https.html new file mode 100644 index 0000000000..4838548580 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-javascript-url-frame-policy.https.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> + +<head> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> +</head> + +<body> + <script> + 'use strict'; + + const script = 'script'; + const testPage = ` + <${script}> + parent.postMessage(document.fullscreenEnabled, '*'); + </${script}> + `; + + function runTest(allow, expectation) { + return new Promise((resolve, reject) => { + window.onmessage = event => resolve(event.data); + + const iframe = document.createElement("iframe"); + iframe.allow = allow; + iframe.src = `javascript: \`${testPage}\``; + document.body.appendChild(iframe); + + }).then(enabled => { + assert_equals(enabled, expectation); + }); + } + + promise_test(() => runTest('fullscreen *', true), + 'allow attribute(container policy) can enable feature on javascript generated document'); + + promise_test(() => runTest("fullscreen 'none'", false), + 'allow attribute(container policy) can disable feature on javascript generated document'); + + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-all.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-all.https.sub.html new file mode 100644 index 0000000000..46ca378d0b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-all.https.sub.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> +</head> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + /* + fullscreen is allowed for all at the top-level document. It can be disabled by + subframes. + */ + 'use strict'; + const same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + const cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html'; + const cross_origin_src = cross_origin + same_origin_src; + + /* ------------------------------------------ + | top-level document | + | ------------------------------------ | + | | same-origin iframe | | + | | ------------------------------ | | + | | | local and remote iframes | | | + | | ------------------------------ | | + | ------------------------------------ | + ------------------------------------------ */ + test_subframe_header_policy('fullscreen', '*', same_origin_src, + {local_all: true, local_self: true, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=*"'); + test_subframe_header_policy('fullscreen', 'self', same_origin_src, + {local_all: true, local_self: true, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=self'); + test_subframe_header_policy('fullscreen', '\\(\\)', same_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=()"'); + + /* ------------------------------------------- + | top-level document | + | ------------------------------------- | + | | cross-origin iframe | | + | | ------------------------------- | | + | | | local and remote iframes | | | + | | ------------------------------- | | + | ------------------------------------- | + ------------------------------------------- */ + test_subframe_header_policy('fullscreen', '*', cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=*"'); + test_subframe_header_policy('fullscreen', 'self', cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=self"'); + test_subframe_header_policy('fullscreen', '\\(\\)', cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=()"'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..2cbb8a82c6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=* diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..54c6e95b6e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-self.https.sub.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<meta name="timeout" content="long"> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + /* + fullscreen is allowed for 'self' at the top-level document and through the + chain of same-origin iframes. It can be enabled by subframes, but otherwise + is disallowed everywhere else. + */ + 'use strict'; + const same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + const cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html'; + const cross_origin_src = cross_origin + same_origin_src; + + /* ------------------------------------------ + | top-level document | + | ------------------------------------ | + | | same-origin iframe | | + | | ------------------------------ | | + | | | local and remote iframes | | | + | | ------------------------------ | | + | ------------------------------------ | + ------------------------------------------ */ + test_subframe_header_policy('fullscreen', '*', same_origin_src, + {local_all: true, local_self: true, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=*"'); + test_subframe_header_policy('fullscreen', 'self', same_origin_src, + {local_all: true, local_self: true, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=self"'); + test_subframe_header_policy('fullscreen', '\\(\\)', same_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=()"'); + + /* ------------------------------------------- + | top-level document | + | ------------------------------------- | + | | cross-origin iframe | | + | | ------------------------------- | | + | | | local and remote iframes | | | + | | ------------------------------- | | + | ------------------------------------- | + ------------------------------------------- */ + test_subframe_header_policy('fullscreen', '*', cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=*"'); + test_subframe_header_policy('fullscreen', 'self', cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=self"'); + test_subframe_header_policy('fullscreen', '\\(\\)', cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=()"'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-self.https.sub.html.sub.headers new file mode 100644 index 0000000000..ff7ae41353 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-allowed-for-self.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=self diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-disallowed-for-all.https.sub.html b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-disallowed-for-all.https.sub.html new file mode 100644 index 0000000000..b6ae51b350 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-disallowed-for-all.https.sub.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<meta name="timeout" content="long"> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + /* + fullscreen is disabled at the top-level document, therefore disabled + everywhere throughout inheritance. + */ + 'use strict'; + const same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + const cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html'; + const cross_origin_src = cross_origin + same_origin_src; + const policies = ['*', 'self', '\\(\\)']; + + for (var i = 0; i < policies.length; i++) { + /* ------------------------------------------ + | top-level document | + | ------------------------------------ | + | | same-origin iframe | | + | | ------------------------------ | | + | | | local and remote iframes | | | + | | ------------------------------ | | + | ------------------------------------ | + ------------------------------------------ */ + test_subframe_header_policy('fullscreen', policies[i], same_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with local iframe on policy "fullscreen=' + + policies[i] + '".'); + + /* ------------------------------------------- + | top-level document | + | ------------------------------------- | + | | cross-origin iframe | | + | | ------------------------------- | | + | | | local and remote iframes | | | + | | ------------------------------- | | + | ------------------------------------- | + ------------------------------------------- */ + test_subframe_header_policy('fullscreen', policies[i], cross_origin_src, + {local_all: false, local_self: false, local_none: false, + remote_all: false, remote_self: false, remote_none: false}, + 'Test nested header policy with remote iframe on policy "fullscreen=' + + policies[i] + '".'); +} + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-disallowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-disallowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..a65abd6e45 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-nested-header-policy-disallowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=() diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-opaque-origin-history.https.html b/testing/web-platform/tests/permissions-policy/permissions-policy-opaque-origin-history.https.html new file mode 100644 index 0000000000..969ca369e1 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-opaque-origin-history.https.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + + function get_response() { + return new Promise(resolve => { + window.addEventListener('message', e => { + resolve(e.data); + }, { once: true }); + }); + } + + promise_test(async () => { + // - opaque-origin-history1.html navigates itself to opaque-origin-history2.html. + // - opaque-origin-history2.html call window.history.back() to navigate + // back to opaque-origin-history1.html + // - opaque-origin-history1.html should still be able to access fullscreen + // feature after the history.back() navigation. + const iframe = document.createElement('iframe'); + // sandbox iframe so that it has opaque origin. + iframe.sandbox = 'allow-scripts'; + iframe.src = 'resources/opaque-origin-history1.sub.https.html'; + iframe.allow = "fullscreen 'src'"; + document.body.appendChild(iframe); + + + assert_equals( + await get_response(), + 'fullscreen enabled in opaque-origin-history1.html', + 'iframe should be able to access fullscreen.' + ); + + iframe.contentWindow.postMessage('redirect', '*'); + + assert_equals( + await get_response(), + 'fullscreen enabled in opaque-origin-history1.html', + 'iframe should still be able to access fullscreen after history.back() navigation.' + ); + }); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/permissions-policy-opaque-origin.https.html b/testing/web-platform/tests/permissions-policy/permissions-policy-opaque-origin.https.html new file mode 100644 index 0000000000..edb6e11a95 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/permissions-policy-opaque-origin.https.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + async_test(t => { + // opaque-origin1.html navigates itself to opaque-origin2.html onload. + // The 'src' shorthand in allow="fullscreen 'src'" should only match + // the initial src of opaque-origin1.html. opaque-origin2.html + // should not get access to fullscreen. + const iframe = document.createElement('iframe'); + iframe.sandbox = 'allow-scripts'; + iframe.src = 'resources/opaque-origin1.sub.https.html'; + iframe.allow = "fullscreen 'src'"; + + window.addEventListener('message', t.step_func_done(e => { + assert_equals(e.data, "fullscreen disabled in opaque-origin2.html"); + })); + + document.body.appendChild(iframe); + }); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html new file mode 100644 index 0000000000..d965fdc5fb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<body> + <script src=/common/media.js></script> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script src=/permissions-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const relative_path = '/permissions-policy/resources/permissions-policy-picture-in-picture.html'; + const base_src = '/permissions-policy/resources/redirect-on-load.html#'; + const same_origin_src = base_src + relative_path; + const cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' + + relative_path; + const header = 'permissions policy allow="picture-in-picture"'; + + async_pip_test(t => { + test_feature_availability( + 'picture-in-picture', t, same_origin_src, + expect_feature_available_default, 'picture-in-picture'); + }, header + ' allows same-origin navigation in an iframe.'); + + async_pip_test(t => { + test_feature_availability( + 'picture-in-picture', t, cross_origin_src, + expect_feature_unavailable_default, 'picture-in-picture'); + }, header + ' disallows cross-origin navigation in an iframe.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy-attribute.https.sub.html b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy-attribute.https.sub.html new file mode 100644 index 0000000000..c8371ab29c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy-attribute.https.sub.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<body> + <script src=/common/media.js></script> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script src=/permissions-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const feature_name = 'permissions policy "picture-in-picture"'; + const header = 'allow="picture-in-picture" attribute'; + + async_pip_test(t => { + test_feature_availability( + 'picture-in-picture', t, same_origin_src, + expect_feature_available_default, 'picture-in-picture'); + }, feature_name + ' can be enabled in same-origin iframe using ' + header); + + async_pip_test(t => { + test_feature_availability( + 'picture-in-picture', t, cross_origin_src, + expect_feature_available_default, 'picture-in-picture'); + }, feature_name + ' can be enabled in cross-origin iframe using ' + header); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..dc85111ed4 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy.https.sub.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<body> + <script src=/common/media.js></script> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script src=/permissions-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'permissions policy header: picture-in-picture *'; + + async_pip_test(t => { + isPictureInPictureAllowed().then(t.step_func_done((result) => { + assert_true(result); + })); + }, header + ' allows the top-level document.'); + + async_pip_test(t => { + test_feature_availability('picture-in-picture', t, same_origin_src, + expect_feature_available_default); + }, header + ' allows same-origin iframes.'); + + async_pip_test(t => { + test_feature_availability('picture-in-picture', t, cross_origin_src, + expect_feature_available_default); + }, header + ' allows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..a23f9332ce --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-allowed-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: picture-in-picture=* diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-default-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/picture-in-picture-default-permissions-policy.https.sub.html new file mode 100644 index 0000000000..17ab1fd2b5 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-default-permissions-policy.https.sub.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<body> + <script src=/common/media.js></script> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script src=/permissions-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Default "picture-in-picture" permissions policy [*]'; + + async_pip_test(t => { + isPictureInPictureAllowed().then(t.step_func_done((result) => { + assert_true(result); + })); + }, header + ' allows the top-level document.'); + + async_pip_test(t => { + test_feature_availability('picture-in-picture', t, same_origin_src, + expect_feature_available_default); + }, header + ' allows same-origin iframes.'); + + async_pip_test(t => { + test_feature_availability('picture-in-picture', t, cross_origin_src, + expect_feature_available_default); + }, header + ' allows cross-origin iframes.'); + + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-disabled-by-permissions-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/picture-in-picture-disabled-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..7140f686cb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-disabled-by-permissions-policy.https.sub.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<body> + <script src=/common/media.js></script> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script src=/permissions-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/resources/permissions-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'permissions policy header: picture-in-picture "none"'; + + async_pip_test(t => { + isPictureInPictureAllowed().then(t.step_func_done((result) => { + assert_false(result); + })); + }, header + ' disallows the top-level document.'); + + async_pip_test(t => { + test_feature_availability('picture-in-picture', t, same_origin_src, + expect_feature_unavailable_default); + }, header + ' disallows same-origin iframes.'); + + async_pip_test(t => { + test_feature_availability('picture-in-picture', t, cross_origin_src, + expect_feature_unavailable_default); + }, header + ' disallows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-disabled-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/picture-in-picture-disabled-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..12fc99b50b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-disabled-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: picture-in-picture=() diff --git a/testing/web-platform/tests/permissions-policy/picture-in-picture-supported-by-permissions-policy.html b/testing/web-platform/tests/permissions-policy/picture-in-picture-supported-by-permissions-policy.html new file mode 100644 index 0000000000..387a878f3b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/picture-in-picture-supported-by-permissions-policy.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<title>Test that picture-in-picture is advertised in the feature list</title> +<link rel="help" href="https://w3c.github.io/webappsec-permissions-policy/#dom-permissions-policy-features"> +<link rel="help" href="https://wicg.github.io/picture-in-picture/#permissions-policy"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(() => { + assert_in_array('picture-in-picture', document.featurePolicy.features()); +}, 'document.featurePolicy.features should advertise picture-in-picture.'); +</script> diff --git a/testing/web-platform/tests/permissions-policy/policy-extends-to-sandbox.html b/testing/web-platform/tests/permissions-policy/policy-extends-to-sandbox.html new file mode 100644 index 0000000000..50bf8a334c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/policy-extends-to-sandbox.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<title>permissions policy treats opaque origins correctly</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + "use strict"; + async_test(t => { + let frame = document.createElement('iframe'); + frame.src = "/permissions-policy/resources/sandbox-self.html"; + frame.allow = "fullscreen"; + frame.sandbox = "allow-scripts"; + + var handle_message = t.step_func(evt => { + if (evt.source === frame.contentWindow) { + assert_equals(evt.data.child, true, "'self' in header should match origin of sandboxed frame."); + assert_equals(evt.data.grandchild, false, "Opaque origins should not match each other."); + document.body.removeChild(frame); + window.removeEventListener('message', handle_message); + t.done(); + } + }); + window.addEventListener('message', handle_message); + document.body.appendChild(frame); + }); +</script> diff --git a/testing/web-platform/tests/permissions-policy/reporting/bluetooth-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-report-only.https.html new file mode 100644 index 0000000000..9688baa5b0 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-report-only.https.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src='/resources/testdriver-vendor.js'></script> + <script src='/bluetooth/resources/bluetooth-test.js'></script> + </head> + <body> + <script> + +function check_report_format([reports, observer]) { + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "bluetooth"); + assert_equals(report.body.disposition, "report"); +}; + +bluetooth_test(() => { + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await navigator.bluetooth.getAvailability(); + check_report_format(await report); + }, 'getAvailability in bluetooth report only mode'); + + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await navigator.bluetooth.getDevices(); + check_report_format(await report); + }, 'getDevices in bluetooth report only mode'); + + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await test_driver.bless('Activate document for bluetooth.requestDevice'); + await promise_rejects_js(t, TypeError, navigator.bluetooth.requestDevice({filters: []}), 'requestDevice() call should fail when no filters are selected.') + + check_report_format(await report); + }, 'requestDevice in bluetooth report only mode'); + + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await test_driver.bless('Activate document for bluetooth.requestLEScan'); + await promise_rejects_js(t, TypeError, navigator.bluetooth.requestLEScan({filters: []}), 'requestLEScan() call should fail when no filters are selected.'); + + check_report_format(await report); + }, 'requestLEScan in bluetooth report only mode'); +}); + </script> + </body> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/reporting/bluetooth-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-report-only.https.html.headers new file mode 100644 index 0000000000..dfa8cf0090 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: bluetooth=()
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/reporting/bluetooth-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-reporting.https.html new file mode 100644 index 0000000000..35b22122b9 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-reporting.https.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src='/resources/testdriver-vendor.js'></script> + <script src='/bluetooth/resources/bluetooth-test.js'></script> + <script src='../resources/permissions-policy-report-json.js'></script> + </head> + <body> + <script> + +function check_report_format([reports, observer]) { + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "bluetooth"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); + check_report_json(report); +}; + +bluetooth_test(() => { + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + try { + await navigator.bluetooth.getAvailability().then(availability => { + assert_false(availability); + }); + } catch(e) { + assert_unreached('getAvailability should return false when Bluetooth access is disallowed.'); + } + check_report_format(await report); + }, 'getAvailability in bluetooth reporting mode'); + + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await promise_rejects_dom( + t, 'SecurityError', navigator.bluetooth.getDevices(), + 'Bluetooth access should not be allowed in this document.'); + check_report_format(await report); + }, 'getDevices in bluetooth reporting mode'); + + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await test_driver.bless('Activate document for bluetooth.requestDevice'); + await promise_rejects_dom( + t, 'SecurityError', navigator.bluetooth.requestDevice(), + 'Bluetooth access should not be allowed in this document.'); + check_report_format(await report); + }, 'requestDevice in bluetooth reporting mode'); + + promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await test_driver.bless('Activate document for bluetooth.requestLEScan'); + await promise_rejects_dom( + t, 'SecurityError', navigator.bluetooth.requestLEScan(), + 'Bluetooth access should not be allowed in this document.'); + check_report_format(await report); + }, 'requestLEScan in bluetooth reporting mode'); +}); + </script> + </body> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/reporting/bluetooth-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-reporting.https.html.headers new file mode 100644 index 0000000000..10b94729dd --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/bluetooth-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: bluetooth=()
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/reporting/camera-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/camera-report-only.https.html new file mode 100644 index 0000000000..2cf20c28a7 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/camera-report-only.https.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="../../mediacapture-streams/permission-helper.js"></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "camera"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await test_driver.bless('Activate document for user media'); + await setMediaPermission("granted", ["camera"]); + await navigator.mediaDevices.getUserMedia({video: true}); + check_report_format(await report); +}, "Camera report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/camera-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/camera-report-only.https.html.headers new file mode 100644 index 0000000000..71a1cca5be --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/camera-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: camera=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/camera-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/camera-reporting.https.html new file mode 100644 index 0000000000..9cf1b18002 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/camera-reporting.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src='/resources/testdriver-vendor.js'></script> + <script src='../../mediacapture-streams/permission-helper.js'></script> + </head> + <body> + <script> +var t = async_test("Camera Report Format"); + +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "camera"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +new ReportingObserver(t.step_func_done(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + +setMediaPermission("granted", ["camera"]).then(() => navigator.mediaDevices.getUserMedia({video: true})).then( + t.unreached_func("UserMedia camera access should not be allowed in this document.") +).catch(() => {}); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/camera-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/camera-reporting.https.html.headers new file mode 100644 index 0000000000..6fcbae1419 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/camera-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: camera=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-report-only.https.html new file mode 100644 index 0000000000..72a1ca2368 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-report-only.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "encrypted-media"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await navigator.requestMediaKeySystemAccess("org.w3.clearkey", + [{ + initDataTypes: ["webm"], + videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}], + }]); + check_report_format(await report); +}, "Encrypted Media report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-report-only.https.html.headers new file mode 100644 index 0000000000..0d5480e2a6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: encrypted-media=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-reporting.https.html new file mode 100644 index 0000000000..32a5a2cc48 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-reporting.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "encrypted-media"); + assert_equals(report.body.disposition, "enforce"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await promise_rejects_dom(t, "SecurityError", + navigator.requestMediaKeySystemAccess("org.w3.clearkey", + [{ + initDataTypes: ["webm"], + videoCapabilities: [{contentType: 'video/webm;codecs="vp8"'}], + }]), + "requestMediaKeySystemAccess() should not be allowed in this document."); + const [reports, observer] = await report; + check_report_format(reports, observer); +}, "Encrypted Media report format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-reporting.https.html.headers new file mode 100644 index 0000000000..9505c02dae --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/encrypted-media-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: encrypted-media=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/fullscreen-report-only.html b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-report-only.html new file mode 100644 index 0000000000..caf31f6b79 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-report-only.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <div id='fs'></div> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "fullscreen"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await test_driver.bless('Activate document for fullscreen'); + await document.getElementById('fs').requestFullscreen(); + check_report_format(await report); + document.exitFullscreen(); +}, "Fullscreen report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/fullscreen-report-only.html.headers b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-report-only.html.headers new file mode 100644 index 0000000000..384d778c9a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-report-only.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: fullscreen=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/fullscreen-reporting.html b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-reporting.html new file mode 100644 index 0000000000..d7b905744d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-reporting.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <div id='fs'></div> + <script> +var observer1; +var observer2; + +var check_report_format = (reports, observer) => { + // Test that observer2 is notified, even if it is disconnected. + observer1.disconnect(); + observer2.disconnect(); + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "fullscreen"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +var check_second_observer = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "fullscreen"); +}; + +async_test(t => { + observer1 = new ReportingObserver(t.step_func(check_report_format), + {types: ['permissions-policy-violation']}); + observer1.observe(); + observer2 = new ReportingObserver(t.step_func_done(check_second_observer), + {types: ['permissions-policy-violation']}); + observer2.observe(); + document.getElementById('fs').requestFullscreen().then(t.unreached_func( + "Fullscreen should not be allowed in this document.")).catch(()=>{}); +}, "Fullscreen Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/fullscreen-reporting.html.headers b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-reporting.html.headers new file mode 100644 index 0000000000..a65abd6e45 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/fullscreen-reporting.html.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-report-only.https.html new file mode 100644 index 0000000000..21fb850391 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-report-only.https.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var sensor_features_verified = { + "accelerometer": false, + "ambient-light-sensor": false, + "magnetometer": false, + "gyroscope": false +}; + +var check_report_format = function(reports, observer) { + // Check each report in this batch. This observer callback may be called + // multiple times before all reports have been processed. + for (const report of reports) { + + // Validate that the reported feature is one of the sensor features, and that + // we have not seen a report for this feature before. + assert_true(sensor_features_verified.hasOwnProperty(report.body.featureId)); + assert_false(sensor_features_verified[report.body.featureId]); + + // Validate the remainder of the report + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "report"); + + sensor_features_verified[report.body.featureId] = true; + } + + // Test is only done when reports for all features have been seen + for (let result of Object.values(sensor_features_verified)) { + if (!result) + return; + } + this.done(); +}; + +async_test(t => { + new ReportingObserver(t.step_func(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + new Accelerometer(); + new AmbientLightSensor(); + new Gyroscope(); + new Magnetometer(); +}, "Generic Sensor report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-report-only.https.html.headers new file mode 100644 index 0000000000..2a51586203 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: ambient-light-sensor=(), accelerometer=(), gyroscope=(), magnetometer=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-reporting.https.html new file mode 100644 index 0000000000..c29c069ee3 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-reporting.https.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var sensor_features_verified = { + "accelerometer": false, + "ambient-light-sensor": false, + "magnetometer": false, + "gyroscope": false +}; + +var check_report_format = function(reports, observer) { + // Check each report in this batch. This observer callback may be called + // multiple times before all reports have been processed. + for (const report of reports) { + + // Validate that the reported feature is one of the sensor features, and that + // we have not seen a report for this feature before. + assert_true(sensor_features_verified.hasOwnProperty(report.body.featureId)); + assert_false(sensor_features_verified[report.body.featureId]); + + // Validate the remainder of the report + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); + + sensor_features_verified[report.body.featureId] = true; + } + + // Test is only done when reports for all features have been seen + for (let result of Object.values(sensor_features_verified)) { + if (!result) + return; + } + this.done(); +}; + +async_test(t => { + new ReportingObserver(t.step_func(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + assert_throws_dom("SecurityError", () => new Accelerometer(), "Constructing sensors should be blocked by policy"); + assert_throws_dom("SecurityError", () => new AmbientLightSensor(), "Constructing sensors should be blocked by policy"); + assert_throws_dom("SecurityError", () => new Gyroscope(), "Constructing sensors should be blocked by policy"); + assert_throws_dom("SecurityError", () => new Magnetometer(), "Constructing sensors should be blocked by policy"); +}, "Generic Sensor Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-reporting.https.html.headers new file mode 100644 index 0000000000..b0ebb72e9c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/generic-sensor-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: ambient-light-sensor=(),accelerometer=(),gyroscope=(),magnetometer=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/geolocation-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/geolocation-report-only.https.html new file mode 100644 index 0000000000..abffd62cfc --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/geolocation-report-only.https.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "geolocation"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + try { + await new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition(resolve, reject); + }); + check_report_format(await report); + } catch (err) { + // In case the getCurrentPosition call was rejected due to user permissions, + // the report should be generated anyway. Wait for it and check the format + // before failing this test. + check_report_format(await report); + throw err; + } +}, "Geolocation report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/geolocation-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/geolocation-report-only.https.html.headers new file mode 100644 index 0000000000..c7e5f25bc5 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/geolocation-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: geolocation=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/geolocation-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/geolocation-reporting.https.html new file mode 100644 index 0000000000..e0eb275bcc --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/geolocation-reporting.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var t = async_test("Geolocation Report Format"); + +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "geolocation"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +new ReportingObserver(t.step_func_done(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + +navigator.geolocation.getCurrentPosition( + t.unreached_func("geolocation should be disabled in this document"), + () => {}); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/geolocation-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/geolocation-reporting.https.html.headers new file mode 100644 index 0000000000..26bfbc2496 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/geolocation-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: geolocation=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/image.bmp b/testing/web-platform/tests/permissions-policy/reporting/image.bmp Binary files differnew file mode 100644 index 0000000000..f2b88690fc --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/image.bmp diff --git a/testing/web-platform/tests/permissions-policy/reporting/microphone-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/microphone-report-only.https.html new file mode 100644 index 0000000000..1b9b87f442 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/microphone-report-only.https.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src='../../mediacapture-streams/permission-helper.js'></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "microphone"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await test_driver.bless('Activate document for user media'); + await setMediaPermission(); + await navigator.mediaDevices.getUserMedia({audio: true}); + check_report_format(await report); +}, "Microphone report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/microphone-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/microphone-report-only.https.html.headers new file mode 100644 index 0000000000..adcf95e9e2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/microphone-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: microphone=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/microphone-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/microphone-reporting.https.html new file mode 100644 index 0000000000..5b4c2121e3 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/microphone-reporting.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src='../../mediacapture-streams/permission-helper.js'></script> + </head> + <body> + <script> +var t = async_test("Microphone Report Format"); + +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "microphone"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +new ReportingObserver(t.step_func_done(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + +setMediaPermission().then(() => navigator.mediaDevices.getUserMedia({audio: true})).then( + t.unreached_func("UserMedia microphone access should not be allowed in this document.") +).catch(() => {}); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/microphone-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/microphone-reporting.https.html.headers new file mode 100644 index 0000000000..ae65ea5c73 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/microphone-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: microphone=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/midi-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/midi-report-only.https.html new file mode 100644 index 0000000000..3aa12dcfb5 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/midi-report-only.https.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "midi"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + try { + await navigator.requestMIDIAccess(); + check_report_format(await report); + } catch (err) { + // In case the requestMIDIAccess call was rejected due to user permissions, + // the report should be generated anyway. Wait for it and check the format + // before failing this test. + check_report_format(await report); + throw err; + } +}, "MIDI report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/midi-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/midi-report-only.https.html.headers new file mode 100644 index 0000000000..10570f6044 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/midi-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: midi=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/midi-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/midi-reporting.https.html new file mode 100644 index 0000000000..6cc07f5371 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/midi-reporting.https.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "midi"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +promise_test(async (t) => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await promise_rejects_dom(t, 'SecurityError', navigator.requestMIDIAccess(), + "MIDI device access should not be allowed in this document."); + const [reports, observer] = await report; + check_report_format(reports, observer); +}, "MIDI Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/midi-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/midi-reporting.https.html.headers new file mode 100644 index 0000000000..1fa88c3a59 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/midi-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: midi=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/payment-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/payment-report-only.https.html new file mode 100644 index 0000000000..f1b1b433f5 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/payment-report-only.https.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src='/resources/testdriver-vendor.js'></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "payment"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + try { + await test_driver.bless(); + const request = new PaymentRequest( + [{ supportedMethods: 'https://example.com/pay' }], + { total: { label: 'Total', amount: { currency: 'USD', value: 0 }}}, + {}); + await request.show() + check_report_format(await report); + } catch (err) { + // In case the show call was rejected, the report should be generated + // anyway. Wait for it and check the format before failing this test. + check_report_format(await report); + throw err; + } +}, "PaymentRequest report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/payment-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/payment-report-only.https.html.headers new file mode 100644 index 0000000000..25be8168ab --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/payment-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: payment=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/payment-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/payment-reporting.https.html new file mode 100644 index 0000000000..f639c2b7c6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/payment-reporting.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='../resources/permissions-policy-report-json.js'></script> + </head> + <body> + <script> +var t = async_test("PaymentRequest Report Format"); + +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "payment"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); + check_report_json(report); +}; + +new ReportingObserver(t.step_func_done(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + +t.step_func(() => { + assert_throws_dom('SecurityError', + () => new PaymentRequest( + [{ supportedMethods: 'https://example.com/pay' }], + { total: { label: 'Total', amount: { currency: 'USD', value: 0 }}}, + {}).show(), + "PaymentRequest API should not be allowed in this document."); +})(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/payment-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/payment-reporting.https.html.headers new file mode 100644 index 0000000000..49f799d138 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/payment-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: payment=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-report-only.html b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-report-only.html new file mode 100644 index 0000000000..0caa2ae953 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-report-only.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/common/media.js'></script> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src='../resources/picture-in-picture.js'></script> + </head> + <body> + <script> +const check_report_format = ([reports, observer]) => { + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "picture-in-picture"); + assert_equals(report.body.disposition, "report"); +}; + +const loadVideo = () => new Promise(resolve => { + const video = document.createElement('video'); + video.src = getVideoURI('/media/movie_5'); + video.addEventListener('loadedmetadata', () => { + resolve(video); + }, { once: true }); +}); + +promise_pip_test(async (t) => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + const videoElement = await loadVideo(); + await test_driver.bless('picture-in-picture'); + await videoElement.requestPictureInPicture(); + check_report_format(await report); +}, "Picture-in-Picture report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-report-only.html.headers b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-report-only.html.headers new file mode 100644 index 0000000000..0f73e39c58 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-report-only.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: picture-in-picture=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-reporting.html b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-reporting.html new file mode 100644 index 0000000000..177e4d5c02 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-reporting.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/common/media.js'></script> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src='../resources/picture-in-picture.js'></script> + </head> + <body> + <script> +const check_report_format = (reports, observer) => { + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "picture-in-picture"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +const loadVideo = () => new Promise(resolve => { + const video = document.createElement('video'); + video.src = getVideoURI('/media/movie_5'); + video.addEventListener('loadedmetadata', () => { + resolve(video); + }, { once: true }); +}); + +promise_pip_test(async (t) => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + const videoElement = await loadVideo(); + await test_driver.bless('picture-in-picture'); + await promise_rejects_dom(t, 'SecurityError', videoElement.requestPictureInPicture(), + "Picture-in-Picture should not be allowed in this document."); + const [reports, observer] = await report; + check_report_format(reports, observer); +}, "Picture-in-Picture Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-reporting.html.headers b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-reporting.html.headers new file mode 100644 index 0000000000..12fc99b50b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/picture-in-picture-reporting.html.headers @@ -0,0 +1 @@ +Permissions-Policy: picture-in-picture=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/screen-wake-lock-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/screen-wake-lock-reporting.https.html new file mode 100644 index 0000000000..4ddfc69f86 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/screen-wake-lock-reporting.https.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> + promise_test(async (t) => { + const reportPromise = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve(reports), + {types: ["permissions-policy-violation"]}).observe(); + }); + // Even though we do not explicitly allow screen wake lock requests in + // testdriver, per spec Permissions Policy checks should happen earlier. + await promise_rejects_dom(t, 'NotAllowedError', navigator.wakeLock.request("screen"), + "Screen Wake Lock should not be allowed in this document."); + const reports = await reportPromise; + + assert_equals(reports.length, 1); + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "screen-wake-lock"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); + }, "Screen Wake Lock Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/screen-wake-lock-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/screen-wake-lock-reporting.https.html.headers new file mode 100644 index 0000000000..66f07f4c1c --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/screen-wake-lock-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: screen-wake-lock=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/serial-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/serial-report-only.https.html new file mode 100644 index 0000000000..3a410362de --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/serial-report-only.https.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <div id='fs'></div> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "serial"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await test_driver.bless('Activate document for serial.requestPort'); + try { + await navigator.serial.requestPort({filters: []}); + assert_unreached('requestPort() call should fail when no port is selected.'); + } catch (e) { + assert_equals(e.code, DOMException.NOT_FOUND_ERR); + } + check_report_format(await report); +}, "requestPort in serial report only mode"); + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await navigator.serial.getPorts(); + check_report_format(await report); +}, "getPorts in serial report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/serial-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/serial-report-only.https.html.headers new file mode 100644 index 0000000000..cee26394d3 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/serial-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: serial=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/serial-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/serial-reporting.https.html new file mode 100644 index 0000000000..c96d8f878a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/serial-reporting.https.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "serial"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + await test_driver.bless('Activate document for serial.requestPort'); + try { + await navigator.serial.requestPort({ filters: [] }); + assert_unreached("Serial port access should not be allowed in this document."); + } catch (e) { + assert_equals(e.code, DOMException.SECURITY_ERR); + } + check_report_format(await report); +}, "requestPort in serial reporting mode"); + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + + try { + await navigator.serial.getPorts(); + assert_unreached("Serial port access should not be allowed in this document."); + } catch (e) { + assert_equals(e.code, DOMException.SECURITY_ERR); + } + check_report_format(await report); +}, "getPorts in serial reporting mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/serial-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/serial-reporting.https.html.headers new file mode 100644 index 0000000000..690b696751 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/serial-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: serial=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-report-only.html b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-report-only.html new file mode 100644 index 0000000000..29b1368a2e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-report-only.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +const check_report_format = ([reports, observer]) => { + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "sync-xhr"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + const xhr = new XMLHttpRequest(); + xhr.open("GET", document.location.href, false); + xhr.send(); + check_report_format(await report); +}, "Sync-xhr report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-report-only.html.headers b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-report-only.html.headers new file mode 100644 index 0000000000..ce914048e1 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-report-only.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: sync-xhr=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-reporting.html b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-reporting.html new file mode 100644 index 0000000000..a8fdb09ece --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-reporting.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='../resources/permissions-policy-report-json.js'></script> + </head> + <body> + <script> +var t = async_test("Sync-xhr Report Format"); + +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "sync-xhr"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); + check_report_json(report); +}; + +new ReportingObserver(t.step_func_done(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + +t.step_func(() => { + var xhr = new XMLHttpRequest(); + xhr.open("GET", document.location.href, false); + assert_throws_dom('NetworkError', + () => xhr.send(), + "Synchronous XHR.send should throw an exception when disabled"); +})(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-reporting.html.headers b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-reporting.html.headers new file mode 100644 index 0000000000..7f375e0b75 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/sync-xhr-reporting.html.headers @@ -0,0 +1 @@ +Permissions-Policy: sync-xhr=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/usb-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/usb-report-only.https.html new file mode 100644 index 0000000000..5a96531115 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/usb-report-only.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + <script src='/resources/testdriver.js'></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <div id='fs'></div> + <script> +var check_report_format = ([reports, observer]) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.body.featureId, "usb"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await test_driver.bless('Activate document for USB'); + await navigator.usb.getDevices(); + check_report_format(await report); +}, "USB report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/usb-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/usb-report-only.https.html.headers new file mode 100644 index 0000000000..f4b92eadd9 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/usb-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: usb=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/usb-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/usb-reporting.https.html new file mode 100644 index 0000000000..1ec5ba4370 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/usb-reporting.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var t = async_test("USB Report Format"); + +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "usb"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +new ReportingObserver(t.step_func_done(check_report_format), + {types: ['permissions-policy-violation']}).observe(); + +navigator.usb.getDevices().then( + t.unreached_func("USB device access should not be allowed in this document.") +).catch(() => {}); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/usb-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/usb-reporting.https.html.headers new file mode 100644 index 0000000000..ff22d62f10 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/usb-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: usb=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/xr-report-only.https.html b/testing/web-platform/tests/permissions-policy/reporting/xr-report-only.https.html new file mode 100644 index 0000000000..f39ecf3432 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/xr-report-only.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +const check_report_format = ([reports, observer]) => { + const report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "xr-spatial-tracking"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "report"); +}; + +promise_test(async t => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + try { + let supported = await navigator.xr.isSessionSupported('immersive-vr'); + } catch (err) { + // IsSessionSupported should only throw with a permissions policy violation; + // however, inline does not trigger the permissions policy, + // so immersive-vr must be used. + assert_unreached("isSessionSupported should not throw in ReportOnly mode"); + } + check_report_format(await report); +}, "XR report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/xr-report-only.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/xr-report-only.https.html.headers new file mode 100644 index 0000000000..141deb4dfc --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/xr-report-only.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy-Report-Only: xr-spatial-tracking=() diff --git a/testing/web-platform/tests/permissions-policy/reporting/xr-reporting.https.html b/testing/web-platform/tests/permissions-policy/reporting/xr-reporting.https.html new file mode 100644 index 0000000000..d87d4fb028 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/xr-reporting.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> + <head> + <script src='/resources/testharness.js'></script> + <script src='/resources/testharnessreport.js'></script> + </head> + <body> + <script> +var check_report_format = (reports, observer) => { + let report = reports[0]; + assert_equals(report.type, "permissions-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "xr-spatial-tracking"); + assert_equals(report.body.sourceFile, document.location.href); + assert_equals(typeof report.body.lineNumber, "number"); + assert_equals(typeof report.body.columnNumber, "number"); + assert_equals(report.body.disposition, "enforce"); +}; + +promise_test(async (t) => { + const report = new Promise(resolve => { + new ReportingObserver((reports, observer) => resolve([reports, observer]), + {types: ['permissions-policy-violation']}).observe(); + }); + await promise_rejects_dom(t, 'SecurityError', + navigator.xr.isSessionSupported('immersive-vr'), + "XR spatial tracking should not be allowed in this document."); + const [reports, observer] = await report; + check_report_format(reports, observer); +}, "XR Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/xr-reporting.https.html.headers b/testing/web-platform/tests/permissions-policy/reporting/xr-reporting.https.html.headers new file mode 100644 index 0000000000..56b8c11a5b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/xr-reporting.https.html.headers @@ -0,0 +1 @@ +Permissions-Policy: xr-spatial-tracking=() diff --git a/testing/web-platform/tests/permissions-policy/resources/autoplay.js b/testing/web-platform/tests/permissions-policy/resources/autoplay.js new file mode 100644 index 0000000000..56780cf6dc --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/autoplay.js @@ -0,0 +1,28 @@ + + +function simulateGesture(t, callback) { + // Get or create the target element. + let target = document.getElementById('target'); + if (!target) { + target = document.createElement('button'); + target.setAttribute('id', 'target'); + document.body.appendChild(target); + } + + // Simulate a gesture in the top frame to remove any gesture based autoplay + // restrictions. + test_driver.click(target).then(callback, t.unreached_func('click failed')); +} + +function isAutoplayAllowed() { + return new Promise((resolve, reject) => { + const video = document.createElement('video'); + video.src = getVideoURI('/media/A4'); + video.play().then(() => resolve(true), (e) => { + if (e.name == 'NotAllowedError') + resolve(false); + else + resolve(true); + }); + }); +} diff --git a/testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html b/testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html new file mode 100644 index 0000000000..4ba512140d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<title>Return fullscreen permissions policy state</title> +<script> + "use strict"; + window.onload = () => { + window.parent.postMessage(document.featurePolicy.allowedFeatures().includes("fullscreen"),"*"); + }; +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html new file mode 100644 index 0000000000..cb1f214f53 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html @@ -0,0 +1,15 @@ +<script> + window.addEventListener('message', e => { + if (e.data == 'redirect') { + location.assign( + "https://{{domains[]}}:{{location[port]}}/permissions-policy/resources/opaque-origin-history2.https.html"); + } + }); + + parent.postMessage( + document.fullscreenEnabled ? + 'fullscreen enabled in opaque-origin-history1.html' : + 'fullscreen disabled in opaque-origin-history1.html', + '*' + ); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html new file mode 100644 index 0000000000..20e63cf48b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html @@ -0,0 +1,3 @@ +<script> + history.back(); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html new file mode 100644 index 0000000000..f8a8c9d1ad --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html @@ -0,0 +1,4 @@ +<script> + location.assign( + "https://{{domains[]}}:{{location[port]}}/permissions-policy/resources/opaque-origin2.https.html"); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html new file mode 100644 index 0000000000..73122ff7f6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html @@ -0,0 +1,8 @@ +<script> + parent.postMessage( + document.fullscreenEnabled ? + 'fullscreen enabled in opaque-origin2.html' : + 'fullscreen disabled in opaque-origin2.html', + '*' + ); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html new file mode 100644 index 0000000000..f4b020273f --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html @@ -0,0 +1,7 @@ +<script> +'use strict'; + +window.onload = function() { + parent.postMessage(document.featurePolicy.allowedFeatures(), '*'); +} +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html new file mode 100644 index 0000000000..665d19bea2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html @@ -0,0 +1,11 @@ +<script src="/common/media.js"></script> +<script src=/permissions-policy/resources/autoplay.js></script> +<script> +'use strict'; + +window.addEventListener('load', () => { + isAutoplayAllowed().then((result) => { + window.parent.postMessage({ enabled: result }, '*'); + }); +}, { once: true }); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html new file mode 100644 index 0000000000..643d6a4ef5 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html @@ -0,0 +1,9 @@ +<script> +'use strict'; + +Promise.resolve().then(() => navigator.getBattery()).then(battery => { + window.parent.postMessage({ type: 'availability-result', enabled: true }, '*'); +}, error => { + window.parent.postMessage({ type: 'availability-result', enabled: false }, '*'); +}); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html new file mode 100644 index 0000000000..2265bd01d2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html @@ -0,0 +1,9 @@ +<script> + 'use strict'; + + navigator.bluetooth.getDevices().then(devices => { + window.parent.postMessage({ type: 'availability-result', enabled: true}, '*'); + }, error => { + window.parent.postMessage({ type: 'availability-result', enabled: false }, '*'); + }); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html new file mode 100644 index 0000000000..10fc45fd93 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html @@ -0,0 +1,20 @@ +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script> +'use strict'; + +(async () => { + try { + // TODO(https://crbug.com/1074482): Cross-origin focus is asynchronous and + // requires user gesture. Implement testing support for cross-origin focus. + window.focus(); // The Clipboard API requires focus. + + await test_driver.set_permission({ name: 'clipboard-read' }, 'granted'); + await navigator.clipboard.readText('test text'); + + window.parent.postMessage({ enabled: true }, "*"); + } catch (e) { + window.parent.postMessage({ enabled: false }, "*"); + } +})(); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html new file mode 100644 index 0000000000..7eb96e3db0 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html @@ -0,0 +1,20 @@ +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script> +'use strict'; + +(async () => { + try { + // TODO(https://crbug.com/1074482): Cross-origin focus is asynchronous and + // requires user gesture. Implement testing support for cross-origin focus. + window.focus(); // The Clipboard API requires focus. + + await test_driver.set_permission({ name: 'clipboard-write' }, 'granted'); + await navigator.clipboard.writeText('test text'); + + window.parent.postMessage({ enabled: true }, "*"); + } catch (e) { + window.parent.postMessage({ enabled: false }, "*"); + } +})(); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html new file mode 100644 index 0000000000..fafe71ee04 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script> +'use strict'; + +window.onload = async function() { + let enabled = true; + try { + const observer = new PressureObserver(() => {}); + await observer.observe("cpu"); + observer.disconnect(); + } catch (e) { + enabled = false; + } + parent.postMessage({ type: 'availability-result', enabled }, '*'); +} +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html new file mode 100644 index 0000000000..59652e2e7a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html @@ -0,0 +1,11 @@ +<script> +"use strict"; + +try { + const sensorName = location.hash.substring(1); + const sensor = new window[sensorName](); + window.parent.postMessage({ enabled: true }, "*"); +} catch (e) { + window.parent.postMessage({ enabled: false }, "*"); +} +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html new file mode 100644 index 0000000000..b858a52392 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html @@ -0,0 +1,22 @@ +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script> + "use strict"; + + Promise.resolve().then(async () => { + test_driver.set_test_context(window.parent); + await test_driver.set_permission( + { name: "geolocation" }, + "granted" + ); + let enabled = true; + try { + await new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition(resolve, reject); + }); + } catch (e) { + enabled = false; + } + window.parent.postMessage({ type: "availability-result", enabled }, "*"); + }); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html new file mode 100644 index 0000000000..5bcc398039 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html @@ -0,0 +1,10 @@ +<script> +'use strict'; + +let worker = new Worker('permissions-policy-idle-detection-worker.js'); + +worker.onmessage = event => { + window.parent.postMessage(event.data, '*'); +}; +worker.postMessage({ type: 'ready' }); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js new file mode 100644 index 0000000000..0d348c72ae --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js @@ -0,0 +1,16 @@ +'use strict'; + +// Dedicated worker +if (typeof postMessage === 'function') { + onmessage = event => { + switch(event.data.type) { + case 'ready': + new IdleDetector().start().then(() => { + postMessage({ type: 'availability-result', enabled: true }); + }, error => { + postMessage ({ type: 'availability-result', enabled: false }); + }); + break; + } + }; +} diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html new file mode 100644 index 0000000000..f21a3851d1 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html @@ -0,0 +1,10 @@ +<script> +'use strict'; + +new IdleDetector().start().then(() => { + window.parent.postMessage({ type: 'availability-result', enabled: true }, '*'); +}, error => { + window.parent.postMessage({ type: 'availability-result', enabled: false }, '*'); +}); + +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html new file mode 100644 index 0000000000..1b965d9c7d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html @@ -0,0 +1,11 @@ +<script> +'use strict'; + +window.onload = function() { + self.queryLocalFonts().then(() => { + parent.postMessage({ type: 'availability-result', enabled: true }, '*'); + }, error => { + parent.postMessage({ type: 'availability-result', enabled: false }, '*'); + }); +} +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html new file mode 100644 index 0000000000..4c012bbae6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<body> +<script> +'use strict'; +const same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html'; +const cross_origin_src = 'https://{{domains[www1]}}:{{ports[https][0]}}' + same_origin_src; +const subframe_header_policy = '?pipe=header(Permissions-Policy,fullscreen='; +const policy_all = '*'; +const policy_self = 'self'; +const policy_none = '\\(\\)'; + +// Messages gathered from subframes. When all subframe messages are gathered, +// it will be send back to top level frame. +const subframe_messages = []; + +let local_frame_all = document.createElement('iframe'); +let local_frame_self = document.createElement('iframe'); +let local_frame_none = document.createElement('iframe'); +local_frame_all.src = same_origin_src + subframe_header_policy + policy_all + ')'; +local_frame_self.src = same_origin_src + subframe_header_policy + policy_self + ')'; +local_frame_none.src = same_origin_src + subframe_header_policy + policy_none + ')'; + +let remote_frame_all = document.createElement('iframe'); +let remote_frame_self = document.createElement('iframe'); +let remote_frame_none = document.createElement('iframe'); +remote_frame_all.src = cross_origin_src + subframe_header_policy + policy_all + ')'; +remote_frame_self.src = cross_origin_src + subframe_header_policy + policy_self + ')'; +remote_frame_none.src = cross_origin_src + subframe_header_policy + policy_none + ')'; + +window.addEventListener('message', function(evt) { + if (evt.source === local_frame_all.contentWindow) { + subframe_messages.push({frame: 'local', policy: policy_all, allowedfeatures: evt.data}); + } else if (evt.source === local_frame_self.contentWindow) { + subframe_messages.push({frame: 'local', policy: policy_self, allowedfeatures: evt.data}); + } else if (evt.source === local_frame_none.contentWindow) { + subframe_messages.push({frame: 'local', policy: policy_none, allowedfeatures: evt.data}); + } else if (evt.source === remote_frame_all.contentWindow) { + subframe_messages.push({frame: 'remote', policy: policy_all, allowedfeatures: evt.data}); + } else if (evt.source === remote_frame_self.contentWindow) { + subframe_messages.push({frame: 'remote', policy: policy_self, allowedfeatures: evt.data}); + } else if (evt.source === remote_frame_none.contentWindow) { + subframe_messages.push({frame: 'remote', policy: policy_none, allowedfeatures: evt.data}); + } + + if (subframe_messages.length == 6) + parent.postMessage(subframe_messages, '*'); +}); + +document.body.appendChild(local_frame_all); +document.body.appendChild(local_frame_self); +document.body.appendChild(local_frame_none); +document.body.appendChild(remote_frame_all); +document.body.appendChild(remote_frame_self); +document.body.appendChild(remote_frame_none); +</script> +</body> + diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html new file mode 100644 index 0000000000..86f0ea3e48 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html @@ -0,0 +1,60 @@ +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/webauthn/helpers.js"></script> +<script> +'use strict'; + +const textEncoder = new TextEncoder(); +let authenticatorArgs = { + protocol: 'ctap2_1', + transport: 'internal', + hasResidentKey: true, + hasUserVerification: true, + isUserVerified: true, +}; + +window.onload = async function() { + await window.test_driver.add_virtual_authenticator(authenticatorArgs); + let enabled = true; + let message = `OK`; + try { + const publicKey = { + rp: { + id: window.location.hostname, + name: 'Joe', + }, + user: { + name: 'user@domain', + id: Uint8Array.from('id', c => c.charCodeAt(0)), + displayName: 'User', + }, + challenge: textEncoder.encode('Enrollment challenge'), + pubKeyCredParams: [{ + type: 'public-key', + alg: -7, // ECDSA, not supported on Windows. + }, { + type: 'public-key', + alg: -257, // RSA, supported on Windows. + }], + authenticatorSelection: { + userVerification: 'required', + residentKey: 'required', + authenticatorAttachment: 'platform', + }, + extensions: { + payment: { + isPayment: true, + }, + } + }; + await window.test_driver.bless('user activation'); + await navigator.credentials.create({ + publicKey + }); + } catch (e) { + enabled = false; + message = e.name + '#' + e.message; + } + parent.postMessage({ type: 'availability-result', enabled, message }, '*'); +} +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html new file mode 100644 index 0000000000..641a5e65b6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html @@ -0,0 +1,17 @@ +<script> +'use strict'; + +window.onload = function() { + var supportedInstruments = [ { supportedMethods: [ 'visa' ] } ]; + var details = { + total: { label: 'Test', amount: { currency: 'USD', value: '5.00' } } + }; + let enabled = true; + try { + new PaymentRequest(supportedInstruments, details); + } catch (e) { + enabled = false; + } + parent.postMessage({ type: 'availability-result', enabled }, '*'); +} +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html new file mode 100644 index 0000000000..e512f17ff0 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html @@ -0,0 +1,11 @@ +<script src=/common/media.js></script> +<script src=/permissions-policy/resources/picture-in-picture.js></script> +<script> +'use strict'; + +window.addEventListener('load', () => { + isPictureInPictureAllowed().then(enabled => { + window.parent.postMessage({ type: 'availability-result', enabled }, '*'); + }); +}, { once: true }); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js new file mode 100644 index 0000000000..ad84ff9ce4 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js @@ -0,0 +1,20 @@ +/** + * @fileoverview functions for ensuring permissions policy report is serializable + */ + +const check_report_json = (report) => { + // Ensures toJSON method exists on report. + assert_equals(typeof report.toJSON, "function"); + const report_json = report.toJSON(); + // Ensures toJSON() call is successful. + assert_equals(report.type, report_json.type); + assert_equals(report.url, report_json.url); + assert_equals(report.body.featureId, report_json.body.featureId); + assert_equals(report.body.disposition, report_json.body.disposition); + assert_equals(report.body.sourceFile, report_json.body.sourceFile); + assert_equals(report.body.lineNumber, report_json.body.lineNumber); + assert_equals(report.body.columnNumber, report_json.body.columnNumber); + // Ensures JSON.stringify() serializes the report correctly. + assert_false(JSON.stringify(report) === "{}"); + assert_equals(JSON.stringify(report), JSON.stringify(report_json)); +}
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html new file mode 100644 index 0000000000..b186632251 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html @@ -0,0 +1,18 @@ +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script> +"use strict"; + +Promise.resolve().then(async () => { + try { + await test_driver.set_permission( + { name: 'screen-wake-lock' }, 'granted'); + + const wakeLock = await navigator.wakeLock.request("screen"); + await wakeLock.release(); + window.parent.postMessage({ type: 'availability-result', enabled: true }, "*"); + } catch (e) { + window.parent.postMessage({ type: 'availability-result', enabled: false }, "*"); + } +}); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html new file mode 100644 index 0000000000..56bcfaede9 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html @@ -0,0 +1,10 @@ +<script> +'use strict'; + +let worker = new Worker('permissions-policy-serial-worker.js'); + +worker.onmessage = event => { + window.parent.postMessage(event.data, '*'); +}; +worker.postMessage({ type: 'ready' }); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js new file mode 100644 index 0000000000..59bb6787dd --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js @@ -0,0 +1,14 @@ +'use strict'; + +// Dedicated worker +if (typeof postMessage === 'function') { + onmessage = event => { + switch(event.data.type) { + case 'ready': + navigator.serial.getPorts().then( + () => postMessage({ type: 'availability-result', enabled: true }), + error => postMessage ({ type: 'availability-result', enabled: false })); + break; + } + }; +} diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html new file mode 100644 index 0000000000..fe25f03482 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html @@ -0,0 +1,9 @@ +<script> +'use strict'; + +navigator.serial.getPorts().then(ports => { + window.parent.postMessage({ type: 'availability-result', enabled: true }, '*'); +}, error => { + window.parent.postMessage({ type: 'availability-result', enabled: false }, '*'); +}); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html new file mode 100644 index 0000000000..e50ac55a27 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html @@ -0,0 +1,10 @@ +<script> +'use strict'; + +let worker = new Worker('permissions-policy-usb-worker.js'); + +worker.onmessage = event => { + window.parent.postMessage(event.data, '*'); +}; +worker.postMessage({ type: 'ready' }); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js new file mode 100644 index 0000000000..97d96e7fb7 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js @@ -0,0 +1,14 @@ +'use strict'; + +// Dedicated worker +if (typeof postMessage === 'function') { + onmessage = event => { + switch(event.data.type) { + case 'ready': + navigator.usb.getDevices().then( + () => postMessage({ type: 'availability-result', enabled: true }), + error => postMessage ({ type: 'availability-result', enabled: false })); + break; + } + }; +} diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html new file mode 100644 index 0000000000..8812ca7db9 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html @@ -0,0 +1,9 @@ +<script> +'use strict'; + +Promise.resolve().then(() => navigator.usb.getDevices()).then(devices => { + window.parent.postMessage({ type: 'availability-result', enabled: true }, '*'); +}, error => { + window.parent.postMessage({ type: 'availability-result', enabled: false }, '*'); +}); +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js new file mode 100644 index 0000000000..00c0bf2326 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js @@ -0,0 +1,460 @@ +// Feature test to avoid timeouts +function assert_permissions_policy_supported() { + assert_not_equals(document.featurePolicy, undefined, + 'permissions policy is supported'); +} +// Tests whether a feature that is enabled/disabled by permissions policy works +// as expected. +// Arguments: +// feature_description: a short string describing what feature is being +// tested. Examples: "usb.GetDevices()", "PaymentRequest()". +// test: test created by testharness. Examples: async_test, promise_test. +// src: URL where a feature's availability is checked. Examples: +// "/permissions-policy/resources/permissions-policy-payment.html", +// "/permissions-policy/resources/permissions-policy-usb.html". +// expect_feature_available: a callback(data, feature_description) to +// verify if a feature is available or unavailable as expected. +// The file under the path "src" defines what "data" is sent back via +// postMessage with type: 'availability-result'. +// Inside the callback, some tests (e.g., EXPECT_EQ, EXPECT_TRUE, etc) +// are run accordingly to test a feature's availability. +// Example: expect_feature_available_default(data, feature_description). +// feature_name: Optional argument, only provided when testing iframe allow +// attribute. "feature_name" is the feature name of a policy controlled +// feature (https://w3c.github.io/webappsec-permissions-policy/#features). +// See examples at: +// https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md +// allow_attribute: Optional argument, only used for testing fullscreen or +// payment: either "allowfullscreen" or "allowpaymentrequest" is passed. +function test_feature_availability( + feature_description, test, src, expect_feature_available, feature_name, + allow_attribute) { + let frame = document.createElement('iframe'); + frame.src = src; + + if (typeof feature_name !== 'undefined') { + frame.allow = frame.allow.concat(";" + feature_name); + } + + if (typeof allow_attribute !== 'undefined') { + frame.setAttribute(allow_attribute, true); + } + + window.addEventListener('message', test.step_func(evt => { + if (evt.source === frame.contentWindow && + evt.data.type === 'availability-result') { + expect_feature_available(evt.data, feature_description); + document.body.removeChild(frame); + test.done(); + } + })); + + document.body.appendChild(frame); +} + +// Default helper functions to test a feature's availability: +function expect_feature_available_default(data, feature_description) { + assert_true(data.enabled, feature_description); +} + +function expect_feature_unavailable_default(data, feature_description) { + assert_false(data.enabled, feature_description); +} + +// This is the same as test_feature_availability() but instead of passing in a +// function to check the result of the message sent back from an iframe, instead +// just compares the result to an expected result passed in. +// Arguments: +// test: test created by testharness. Examples: async_test, promise_test. +// src: the URL to load in an iframe in which to test the feature. +// expected_result: the expected value to compare to the data passed back +// from the src page by postMessage. +// allow_attribute: Optional argument, only provided when an allow +// attribute should be specified on the iframe. +function test_feature_availability_with_post_message_result( + test, src, expected_result, allow_attribute) { + const test_result = ({ name, message }, feature_description) => { + assert_equals(name, expected_result, message + '.'); + }; + test_feature_availability(null, test, src, test_result, allow_attribute); +} + +// If this page is intended to test the named feature (according to the URL), +// tests the feature availability and posts the result back to the parent. +// Otherwise, does nothing. +async function test_feature_in_iframe(feature_name, feature_promise_factory) { + if (location.hash.endsWith(`#${feature_name}`)) { + let message = 'Available'; + let name = '#OK'; + try { + await feature_promise_factory(); + } catch (e) { + ({ name, message } = e); + } + window.parent.postMessage( + { type: 'availability-result', name, message }, '*'); + } +} + +// Returns true if the URL for this page indicates that it is embedded in an +// iframe. +function page_loaded_in_iframe() { + return new URLSearchParams(location.search).get('in-iframe'); +} + +// Returns a same-origin (relative) URL suitable for embedding in an iframe for +// testing the availability of the feature. +function same_origin_url(feature_name) { + // Add an "in-iframe" query parameter so that we can detect the iframe'd + // version of the page and testharness script loading can be disabled in + // that version, as required for use of testdriver in non-toplevel browsing + // contexts. + return location.pathname + '?in-iframe=yes#' + feature_name; +} + +// Returns a cross-origin (absolute) URL suitable for embedding in an iframe for +// testing the availability of the feature. +function cross_origin_url(base_url, feature_name) { + return base_url + same_origin_url(feature_name); +} + +// This function runs all permissions policy tests for a particular feature that +// has a default policy of "self". This includes testing: +// 1. Feature usage succeeds by default in the top level frame. +// 2. Feature usage succeeds by default in a same-origin iframe. +// 3. Feature usage fails by default in a cross-origin iframe. +// 4. Feature usage succeeds when an allow attribute is specified on a +// cross-origin iframe. +// 5. Feature usage fails when an allow attribute is specified on a +// same-origin iframe with a value of "feature-name 'none'". +// +// The same page which called this function will be loaded in the iframe in +// order to test feature usage there. When this function is called in that +// context it will simply run the feature and return a result back via +// postMessage. +// +// Arguments: +// cross_origin: A cross-origin URL base to be used to load the page which +// called into this function. +// feature_name: The name of the feature as it should be specified in an +// allow attribute. +// error_name: If feature usage does not succeed, this is the string +// representation of the error that will be passed in the rejected +// promise. +// feature_promise_factory: A function which returns a promise which tests +// feature usage. If usage succeeds, the promise should resolve. If it +// fails, the promise should reject with an error that can be +// represented as a string. +function run_all_fp_tests_allow_self( + cross_origin, feature_name, error_name, feature_promise_factory) { + // This may be the version of the page loaded up in an iframe. If so, just + // post the result of running the feature promise back to the parent. + if (page_loaded_in_iframe()) { + test_feature_in_iframe(feature_name, feature_promise_factory); + return; + } + + // Run the various tests. + // 1. Allowed in top-level frame. + promise_test( + () => feature_promise_factory(), + 'Default "' + feature_name + + '" permissions policy ["self"] allows the top-level document.'); + + // 2. Allowed in same-origin iframe. + const same_origin_frame_pathname = same_origin_url(feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, '#OK'); + }, + 'Default "' + feature_name + + '" permissions policy ["self"] allows same-origin iframes.'); + + // 3. Blocked in cross-origin iframe. + const cross_origin_frame_url = cross_origin_url(cross_origin, feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, error_name); + }, + 'Default "' + feature_name + + '" permissions policy ["self"] disallows cross-origin iframes.'); + + // 4. Allowed in cross-origin iframe with "allow" attribute. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, '#OK', feature_name); + }, + 'permissions policy "' + feature_name + + '" can be enabled in cross-origin iframes using "allow" attribute.'); + + // 5. Blocked in same-origin iframe with "allow" attribute set to 'none'. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, error_name, + feature_name + " 'none'"); + }, + 'permissions policy "' + feature_name + + '" can be disabled in same-origin iframes using "allow" attribute.'); +} + +// This function runs all permissions policy tests for a particular feature that +// has a default policy of "*". This includes testing: +// 1. Feature usage succeeds by default in the top level frame. +// 2. Feature usage succeeds by default in a same-origin iframe. +// 3. Feature usage succeeds by default in a cross-origin iframe. +// 4. Feature usage fails when an allow attribute is specified on a +// cross-origin iframe with a value of "feature-name 'none'". +// 5. Feature usage fails when an allow attribute is specified on a +// same-origin iframe with a value of "feature-name 'none'". +// +// The same page which called this function will be loaded in the iframe in +// order to test feature usage there. When this function is called in that +// context it will simply run the feature and return a result back via +// postMessage. +// +// Arguments: +// cross_origin: A cross-origin URL base to be used to load the page which +// called into this function. +// feature_name: The name of the feature as it should be specified in an +// allow attribute. +// error_name: If feature usage does not succeed, this is the string +// representation of the error that will be passed in the rejected +// promise. +// feature_promise_factory: A function which returns a promise which tests +// feature usage. If usage succeeds, the promise should resolve. If it +// fails, the promise should reject with an error that can be +// represented as a string. +function run_all_fp_tests_allow_all( + cross_origin, feature_name, error_name, feature_promise_factory) { + // This may be the version of the page loaded up in an iframe. If so, just + // post the result of running the feature promise back to the parent. + if (page_loaded_in_iframe()) { + test_feature_in_iframe(feature_name, feature_promise_factory); + return; + } + + // Run the various tests. + // 1. Allowed in top-level frame. + promise_test( + () => feature_promise_factory(), + 'Default "' + feature_name + + '" permissions policy ["*"] allows the top-level document.'); + + // 2. Allowed in same-origin iframe. + const same_origin_frame_pathname = same_origin_url(feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, '#OK'); + }, + 'Default "' + feature_name + + '" permissions policy ["*"] allows same-origin iframes.'); + + // 3. Allowed in cross-origin iframe. + const cross_origin_frame_url = cross_origin_url(cross_origin, feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, '#OK'); + }, + 'Default "' + feature_name + + '" permissions policy ["*"] allows cross-origin iframes.'); + + // 4. Blocked in cross-origin iframe with "allow" attribute set to 'none'. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, error_name, + feature_name + " 'none'"); + }, + 'permissions policy "' + feature_name + + '" can be disabled in cross-origin iframes using "allow" attribute.'); + + // 5. Blocked in same-origin iframe with "allow" attribute set to 'none'. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, error_name, + feature_name + " 'none'"); + }, + 'permissions policy "' + feature_name + + '" can be disabled in same-origin iframes using "allow" attribute.'); +} + +// This function tests that a subframe's document policy allows a given feature. +// A feature is allowed in a frame either through inherited policy or specified +// by iframe allow attribute. +// Arguments: +// test: test created by testharness. Examples: async_test, promise_test. +// feature: feature name that should be allowed in the frame. +// src: the URL to load in the frame. +// allow: the allow attribute (container policy) of the iframe +function test_allowed_feature_for_subframe(message, feature, src, allow) { + let frame = document.createElement('iframe'); + if (typeof allow !== 'undefined') { + frame.allow = allow; + } + promise_test(function() { + assert_permissions_policy_supported(); + frame.src = src; + return new Promise(function(resolve, reject) { + window.addEventListener('message', function handler(evt) { + resolve(evt.data); + }, { once: true }); + document.body.appendChild(frame); + }).then(function(data) { + assert_true(data.includes(feature), feature); + }); + }, message); +} + +// This function tests that a subframe's document policy disallows a given +// feature. A feature is allowed in a frame either through inherited policy or +// specified by iframe allow attribute. +// Arguments: +// test: test created by testharness. Examples: async_test, promise_test. +// feature: feature name that should not be allowed in the frame. +// src: the URL to load in the frame. +// allow: the allow attribute (container policy) of the iframe +function test_disallowed_feature_for_subframe(message, feature, src, allow) { + let frame = document.createElement('iframe'); + if (typeof allow !== 'undefined') { + frame.allow = allow; + } + promise_test(function() { + assert_permissions_policy_supported(); + frame.src = src; + return new Promise(function(resolve, reject) { + window.addEventListener('message', function handler(evt) { + resolve(evt.data); + }, { once: true }); + document.body.appendChild(frame); + }).then(function(data) { + assert_false(data.includes(feature), feature); + }); + }, message); +} + +// This function tests that a subframe with header policy defined on a given +// feature allows and disallows the feature as expected. +// Arguments: +// feature: feature name. +// frame_header_policy: either *, self or \\(\\), defines the frame +// document's header policy on |feature|. +// '(' and ')' need to be escaped because of server end +// header parameter syntax limitation. +// src: the URL to load in the frame. +// test_expects: contains 6 expected results of either |feature| is allowed +// or not inside of a local or remote iframe nested inside +// the subframe given the header policy to be either *, +// self, or (). +// test_name: name of the test. +function test_subframe_header_policy( + feature, frame_header_policy, src, test_expects, test_name) { + let frame = document.createElement('iframe'); + promise_test(function() { + assert_permissions_policy_supported() + frame.src = src + '?pipe=sub|header(Permissions-Policy,' + feature + '=' + + frame_header_policy + ')'; + return new Promise(function(resolve) { + window.addEventListener('message', function handler(evt) { + resolve(evt.data); + }); + document.body.appendChild(frame); + }).then(function(results) { + for (var j = 0; j < results.length; j++) { + var data = results[j]; + + function test_result(message, test_expect) { + if (test_expect) { + assert_true(data.allowedfeatures.includes(feature), message); + } else { + assert_false(data.allowedfeatures.includes(feature), message); + } + } + + if (data.frame === 'local') { + if (data.policy === '*') { + test_result('local_all:', test_expects.local_all); + } + if (data.policy === 'self') { + test_result('local_self:', test_expects.local_self); + } + if (data.policy === '\\(\\)') { + test_result('local_none:', test_expects.local_none); + } + } + + if (data.frame === 'remote') { + if (data.policy === '*') { + test_result('remote_all:', test_expects.remote_all); + } + if (data.policy === 'self') { + test_result('remote_self:', test_expects.remote_self); + } + if (data.policy === '\\(\\)') { + test_result('remote_none:', test_expects.remote_none); + } + } + } + }); + }, test_name); +} + +// This function tests that frame policy allows a given feature correctly. A +// feature is allowed in a frame either through inherited policy or specified +// by iframe allow attribute. +// Arguments: +// feature: feature name. +// src: the URL to load in the frame. If undefined, the iframe will have a +// srcdoc="" attribute +// test_expect: boolean value of whether the feature should be allowed. +// allow: optional, the allow attribute (container policy) of the iframe. +// allowfullscreen: optional, boolean value of allowfullscreen attribute. +// sandbox: optional boolean. If true, the frame will be sandboxed (with +// allow-scripts, so that tests can run in it.) +function test_frame_policy( + feature, src, srcdoc, test_expect, allow, allowfullscreen, sandbox) { + let frame = document.createElement('iframe'); + document.body.appendChild(frame); + // frame_policy should be dynamically updated as allow and allowfullscreen is + // updated. + var frame_policy = frame.permissionsPolicy; + if (typeof allow !== 'undefined') { + frame.setAttribute('allow', allow); + } + if (!!allowfullscreen) { + frame.setAttribute('allowfullscreen', true); + } + if (!!sandbox) { + frame.setAttribute('sandbox', 'allow-scripts'); + } + if (!!src) { + frame.src = src; + } + if (!!srcdoc) { + frame.srcdoc = "<h1>Hello world!</h1>"; + } + if (test_expect) { + assert_true(frame_policy.allowedFeatures().includes(feature)); + } else { + assert_false(frame_policy.allowedFeatures().includes(feature)); + } +} + +function expect_reports(report_count, policy_name, description) { + async_test(t => { + var num_received_reports = 0; + new ReportingObserver(t.step_func((reports, observer) => { + const relevant_reports = reports.filter(r => (r.body.featureId === policy_name)); + num_received_reports += relevant_reports.length; + if (num_received_reports >= report_count) { + t.done(); + } + }), {types: ['permissions-policy-violation'], buffered: true}).observe(); + }, description); +} diff --git a/testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js b/testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js new file mode 100644 index 0000000000..1bf3c1c12a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js @@ -0,0 +1,30 @@ +function async_pip_test(func, name) { + async_test(t => { + assert_true('pictureInPictureEnabled' in document, 'Picture-in-Picture API is available'); + func(t); + }, name); +} + +function promise_pip_test(func, name) { + promise_test(async t => { + assert_true('pictureInPictureEnabled' in document, 'Picture-in-Picture API is available'); + return func(t); + }, name); +} + +function isPictureInPictureAllowed() { + return new Promise(resolve => { + let video = document.createElement('video'); + video.src = getVideoURI('/media/movie_5'); + video.onloadedmetadata = () => { + video.requestPictureInPicture() + .then(() => resolve(document.pictureInPictureEnabled)) + .catch(e => { + if (e.name == 'NotAllowedError') + resolve(document.pictureInPictureEnabled); + else + resolve(false); + }); + }; + }); +} diff --git a/testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html b/testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html new file mode 100644 index 0000000000..54d3cf55b1 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<body> +<script> +// Automatically redirects the page to a new URL on load. +// Load this document with a URL like: +// "permissions-policy/resources/redirect-on-load.html#https://www.example.com/" +window.onload = function () { + document.location = document.location.hash.substring(1); +} +</script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html new file mode 100644 index 0000000000..8240de99c6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>Return fullscreen permissions policy state from self and a sandboxed child frame</title> +<script> + "use strict"; + window.onload = () => { + let frame = document.createElement('iframe'); + frame.src = "/permissions-policy/resources/nested-sandbox.html"; + frame.sandbox = "allow-scripts"; + + var handle_message = evt => { + if (evt.source === frame.contentWindow) { + window.parent.postMessage({ + "child": document.featurePolicy.allowedFeatures().includes("fullscreen"), + "grandchild": evt.data + },"*"); + document.body.removeChild(frame); + window.removeEventListener('message', handle_message); + } + }; + window.addEventListener('message', handle_message); + document.body.appendChild(frame); + }; +</script> diff --git a/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers new file mode 100644 index 0000000000..ff7ae41353 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=self diff --git a/testing/web-platform/tests/permissions-request/META.yml b/testing/web-platform/tests/permissions-request/META.yml new file mode 100644 index 0000000000..b30db1cfb6 --- /dev/null +++ b/testing/web-platform/tests/permissions-request/META.yml @@ -0,0 +1 @@ +spec: https://wicg.github.io/permissions-request/ diff --git a/testing/web-platform/tests/permissions-request/idlharness.any.js b/testing/web-platform/tests/permissions-request/idlharness.any.js new file mode 100644 index 0000000000..b393b1b78a --- /dev/null +++ b/testing/web-platform/tests/permissions-request/idlharness.any.js @@ -0,0 +1,21 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +'use strict'; + +idl_test( + ['permissions-request'], + ['permissions', 'html', 'dom'], + async idl_array => { + if (self.GLOBAL.isWorker()) { + idl_array.add_objects({ WorkerNavigator: ['navigator'] }); + } else { + idl_array.add_objects({ Navigator: ['navigator'] }); + } + + idl_array.add_objects({ + Permissions: ['navigator.permissions'], + }); + } +); diff --git a/testing/web-platform/tests/permissions-revoke/META.yml b/testing/web-platform/tests/permissions-revoke/META.yml new file mode 100644 index 0000000000..16203fe4d7 --- /dev/null +++ b/testing/web-platform/tests/permissions-revoke/META.yml @@ -0,0 +1 @@ +spec: https://wicg.github.io/permissions-revoke/ diff --git a/testing/web-platform/tests/permissions-revoke/idlharness.any.js b/testing/web-platform/tests/permissions-revoke/idlharness.any.js new file mode 100644 index 0000000000..51c63aadef --- /dev/null +++ b/testing/web-platform/tests/permissions-revoke/idlharness.any.js @@ -0,0 +1,21 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +'use strict'; + +idl_test( + ['permissions-revoke'], + ['permissions', 'html', 'dom'], + async idl_array => { + if (self.GLOBAL.isWorker()) { + idl_array.add_objects({ WorkerNavigator: ['navigator'] }); + } else { + idl_array.add_objects({ Navigator: ['navigator'] }); + } + + idl_array.add_objects({ + Permissions: ['navigator.permissions'], + }); + } +); diff --git a/testing/web-platform/tests/permissions/META.yml b/testing/web-platform/tests/permissions/META.yml new file mode 100644 index 0000000000..6a05abee46 --- /dev/null +++ b/testing/web-platform/tests/permissions/META.yml @@ -0,0 +1,4 @@ +spec: https://w3c.github.io/permissions/ +suggested_reviewers: + - miketaylr + - marcoscaceres diff --git a/testing/web-platform/tests/permissions/all-permissions.html b/testing/web-platform/tests/permissions/all-permissions.html new file mode 100644 index 0000000000..9ad014bc6e --- /dev/null +++ b/testing/web-platform/tests/permissions/all-permissions.html @@ -0,0 +1,43 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test all known permissions support</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="log"></div> + +<script> + // These are marked "at risk" in the spec... + const atRisk = [ + "accelerometer", + "ambient-light-sensor", + "background-fetch", + "background-sync", + "bluetooth", + "gyroscope", + "magnetometer", + "midi", + "nfc", + "screen-wake-lock", + "camera", + "display-capture", + "microphone", + "speaker-selection", + "xr-spatial-tracking", + ]; + + // These are known to be supported by multiple engines... + const permissions = [ + "geolocation", + "notifications", + "persistent-storage", + "push", + ] + + for (const name of [...permissions, ...atRisk]) { + promise_test(async (test) => { + const status = await navigator.permissions.query({ name }); + assert_true(status instanceof PermissionStatus); + //assert_equals(status.name, name, `permission's name should be "${name}"`); + }, `Query "${name}" permission`); + } +</script> diff --git a/testing/web-platform/tests/permissions/feature-policy-permissions-query.html b/testing/web-platform/tests/permissions/feature-policy-permissions-query.html new file mode 100644 index 0000000000..bd152e973e --- /dev/null +++ b/testing/web-platform/tests/permissions/feature-policy-permissions-query.html @@ -0,0 +1,11 @@ +<script> +'use strict'; + +Promise.resolve().then(() => navigator.permissions.query({name:'geolocation'})) + .then(permissionStatus => { + window.parent.postMessage({ state: permissionStatus.state }, '*'); +}, error => { + window.parent.postMessage({ state: null }, '*'); +}); +</script> + diff --git a/testing/web-platform/tests/permissions/idlharness.any.js b/testing/web-platform/tests/permissions/idlharness.any.js new file mode 100644 index 0000000000..ff0a969bad --- /dev/null +++ b/testing/web-platform/tests/permissions/idlharness.any.js @@ -0,0 +1,27 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js + +// https://w3c.github.io/permissions/#idl-index + +"use strict"; + +idl_test( + ['permissions'], + ['html', 'dom'], + async idl_array => { + try { + self.permissionStatus = await navigator.permissions.query({ name: "geolocation" }); + } catch (e) {} + + if (self.GLOBAL.isWorker()) { + idl_array.add_objects({ WorkerNavigator: ['navigator'] }); + } else { + idl_array.add_objects({ Navigator: ['navigator'] }); + } + + idl_array.add_objects({ + Permissions: ['navigator.permissions'], + PermissionStatus: ['permissionStatus'] + }); + } +); diff --git a/testing/web-platform/tests/permissions/midi-permission.html b/testing/web-platform/tests/permissions/midi-permission.html new file mode 100644 index 0000000000..1d75e1c883 --- /dev/null +++ b/testing/web-platform/tests/permissions/midi-permission.html @@ -0,0 +1,35 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test WebIDL conversion when querying the "midi" permission</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="log"></div> + +<script> + promise_test(async (test) => { + let calledCount = 0; + const status = await navigator.permissions.query({ + get name() { + calledCount++; + return "midi"; + }, + }); + assert_true(status instanceof PermissionStatus); + assert_equals(status.name, "midi", `permission's name should be "midi"`); + // + // First call should be from: + // + // Let rootDesc be the object permissionDesc refers to, converted to an + // IDL value of type PermissionDescriptor. + // + // Second from: + // + // Let typedDescriptor be the object permissionDesc refers to, + // converted to an IDL value of rootDesc's name's permission descriptor + // type. + // + // See: https://w3c.github.io/permissions/#query-method + // + assert_equals(calledCount, 2, "midi permission should be converted twice"); + }, `querying the "midi" permission requires two WebIDL conversions`); +</script> diff --git a/testing/web-platform/tests/permissions/non-fully-active.https.html b/testing/web-platform/tests/permissions/non-fully-active.https.html new file mode 100644 index 0000000000..1c11afa3d9 --- /dev/null +++ b/testing/web-platform/tests/permissions/non-fully-active.https.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Geolocation Test: non-fully active document</title> +<link rel="help" href="https://github.com/w3c/permissions/pull/365" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<body></body> +<script> + // Creates the iframe, wait for it to load... + async function attachIframe() { + const iframe = document.createElement("iframe"); + await new Promise((resolve) => { + iframe.src = "resources/empty.html"; + iframe.addEventListener("load", resolve, { once: true }); + document.body.appendChild(iframe); + }); + return iframe; + } + + promise_test(async (t) => { + const iframe = await attachIframe(); + + // Steal the needed references + const { permissions } = iframe.contentWindow.navigator; + const TypeErrorCtor = iframe.contentWindow.TypeError; + const DOMExceptionCtor = iframe.contentWindow.DOMException; + + // Let's check that ordering is correct. + await promise_rejects_js( + t, + TypeErrorCtor, + permissions.query({ name: "xxxxx-not-supported" }), + "query() should reject if the feature is not supported" + ); + + // no longer fully active, let's try that again... + iframe.remove(); + + // Now, let's try with Geolocation as it's supported by all browsers. + await promise_rejects_dom( + t, + "InvalidStateError", + DOMExceptionCtor, + permissions.query({ name: "whatever" }), + "must reject in the right global when the document is not fully active" + ); + + // Re-attach, and go back to fully active. + document.body.appendChild(iframe); + await new Promise((resolve) => + iframe.addEventListener("load", resolve, { once: true }) + ); + + // And we are back to fully active, so this should not reject. + const status = await iframe.contentWindow.navigator.permissions.query({ + name: "geolocation", + }); + assert_equals(status.name, "geolocation"); + iframe.remove(); + }, "Trying to query() a non-fully active document rejects with a InvalidStateError"); + + promise_test(async (t) => { + // Create the iframe, wait for it to load... + const iframe = await attachIframe(); + + // Get the status + const initialStatus = + await iframe.contentWindow.navigator.permissions.query({ + name: "geolocation", + }); + + assert_true("onchange" in initialStatus, "onchange is supported"); + + // Let's check events are firing... + await new Promise(async (resolve) => { + const newState = initialStatus.state === "prompt" ? "denied" : "granted"; + initialStatus.addEventListener("change", resolve, { once: true }); + await test_driver.set_permission({ name: "geolocation" }, newState); + }); + + // No longer fully active... + iframe.remove(); + + await new Promise(async (resolve, reject) => { + // Gets dropped on the floor. + initialStatus.addEventListener("change", reject, { once: true }); + await test_driver.set_permission({ name: "geolocation" }, "prompt"); + // Try to force it synthetically. This would trigger the event synchronously. + initialStatus.dispatchEvent(new Event("change")); + resolve(); + }); + + // Re-attach, and go back to fully active. + document.body.appendChild(iframe); + await new Promise((resolve) => + iframe.addEventListener("load", resolve, { once: true }) + ); + + // Finally, let's recheck that permission state. + const finalStatus = await iframe.contentWindow.navigator.permissions.query({ + name: "geolocation", + }); + assert_equals(finalStatus.state, "prompt", "expected prompt state"); + iframe.remove(); + }, "Permission change events shouldn't fire on non-fully active document"); +</script> diff --git a/testing/web-platform/tests/permissions/permissions-cg.https.html b/testing/web-platform/tests/permissions/permissions-cg.https.html new file mode 100644 index 0000000000..7a2d8dcbb4 --- /dev/null +++ b/testing/web-platform/tests/permissions/permissions-cg.https.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test PermissionStatus's name attribute.</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/common/gc.js"></script> + +<script> +promise_test(async () => { + const { state: initialState } = await navigator.permissions.query({ + name: "geolocation", + }); + + const pass = new Promise(async (resolve) => { + let status = await navigator.permissions.query({ + name: "geolocation", + }); + status.addEventListener("change", resolve, { once: true }); + + status = null; + await garbageCollect(); + + // Will cause change event to be dispatched. + await test_driver.set_permission({ name: "geolocation" }, "granted"); + }); + + // Wait for the change event to be dispatched. + await pass; + + // Reset the permission to its default value. + await test_driver.set_permission({ name: "geolocation" }, initialState); +}, "status is not garbage collected when it goes out of scope"); + +</script> diff --git a/testing/web-platform/tests/permissions/permissions-garbage-collect.https.html b/testing/web-platform/tests/permissions/permissions-garbage-collect.https.html new file mode 100644 index 0000000000..cd650dc8f2 --- /dev/null +++ b/testing/web-platform/tests/permissions/permissions-garbage-collect.https.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Test Permission garbage collection persistance.</title> +<link rel="help" href="https://github.com/w3c/permissions/pull/256" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<body> + <script> + async function createABunchOfGarbage() { + const promises = []; + for (let i = 0; i < 25; i++) { + const promise = new Promise((r) => { + const iframe = document.createElement("iframe"); + iframe.onload = () => r(iframe); + iframe.src = "about:blank"; + document.body.appendChild(iframe); + }); + promises.push(promise); + } + const iframes = await Promise.all(promises); + iframes.forEach((iframe) => iframe.remove()); + } + + promise_test(async (t) => { + t.add_cleanup(() => { + return test_driver.set_permission({ name: "geolocation" }, "prompt"); + }); + + const eventPromise = new Promise( + async (r) => { + // we create the status here, but it goes out of scope + // at the end of the function. Thus, we assume it will be + // garbage collected. + const status = await navigator.permissions.query({ + name: "geolocation", + }); + status.addEventListener("change", r); + }, + { once: true } + ); + + // Maybe got garbage collected. + await createABunchOfGarbage(); + + // Causes event to fire. + await test_driver.set_permission({ name: "geolocation" }, "granted"); + await eventPromise; + }, "Events fire even if the status object is garbage collected"); + </script> +</body> diff --git a/testing/web-platform/tests/permissions/permissions-query-feature-policy-attribute.https.sub.html b/testing/web-platform/tests/permissions/permissions-query-feature-policy-attribute.https.sub.html new file mode 100644 index 0000000000..1d7333d9b5 --- /dev/null +++ b/testing/web-platform/tests/permissions/permissions-query-feature-policy-attribute.https.sub.html @@ -0,0 +1,75 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test permissions query againts feature policy allow attribute</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="log"></div> + +<script> + "use strict"; + + function test_permissions_query( + feature_description, test, src, expect_state, allow_attribute) { + let frame = document.createElement('iframe'); + frame.src = src; + + if (typeof allow_attribute !== 'undefined') { + frame.allow = allow_attribute; + } + + window.addEventListener('message', test.step_func(function handler(evt) { + if (evt.source === frame.contentWindow) { + assert_equals(evt.data.state, expect_state, feature_description); + document.body.removeChild(frame); + window.removeEventListener('message', handler); + test.done(); + } + })); + + document.body.appendChild(frame); + } + + const same_origin_src = + "/permissions/feature-policy-permissions-query.html"; + const cross_origin_src = + "https://{{domains[www]}}:{{ports[https][0]}}" + same_origin_src; + + async_test(t => { + test_permissions_query( + 'navigator.permissions.query("geolocation")', + t, + same_origin_src, + "prompt", + "geolocation" + ); + }, 'Permissions.state is "prompt" with allow="geolocation" in same-origin iframes.'); + + async_test(t => { + test_permissions_query( + 'navigator.permissions.query("geolocation")', + t, + cross_origin_src, + "prompt", + "geolocation" + ); + }, 'Permissions.state is "prompt" with allow="geolocation" in cross-origin iframes.'); + + async_test(t => { + test_permissions_query( + 'navigator.permissions.query("geolocation")', + t, + same_origin_src, + "prompt" + ); + }, 'Permission.state is "prompt" in same-origin iframes.'); + + async_test(t => { + test_permissions_query( + 'navigator.permissions.query("geolocation")', + t, + cross_origin_src, + "denied" + ); + }, 'Permission.state is "denied" in cross-origin iframes.'); + +</script> diff --git a/testing/web-platform/tests/permissions/permissionsstatus-name.html b/testing/web-platform/tests/permissions/permissionsstatus-name.html new file mode 100644 index 0000000000..db1d1bb83e --- /dev/null +++ b/testing/web-platform/tests/permissions/permissionsstatus-name.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<title>Test PermissionStatus's name attribute.</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id="log"></div> + +<script> +promise_test(async () => { + const result = await navigator.permissions.query({ + name: "geolocation", + }); + assert_equals(result.name, "geolocation", "Name was geolocation"); +}); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions/resources/empty.html b/testing/web-platform/tests/permissions/resources/empty.html new file mode 100644 index 0000000000..b0653f8c0c --- /dev/null +++ b/testing/web-platform/tests/permissions/resources/empty.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Just a support file</title> |