diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/permissions-policy | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
207 files changed, 6196 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/permissions-policy-header-host-wildcard.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html new file mode 100644 index 0000000000..211ca7445d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + test(function() { + var wildcard_origin = 'https://*:{{ports[https][0]}}'; + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [wildcard_origin]); + assert_true(document.featurePolicy.allowsFeature('fullscreen')); + }, 'Test wildcard host policy works as expected'); + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html.sub.headers new file mode 100644 index 0000000000..81d0de836e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-host-wildcard.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https://*:{{ports[https][0]}}") diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html new file mode 100644 index 0000000000..adb657023e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + test(function() { + var wildcard_origin = 'https://{{domains[]}}:*'; + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [wildcard_origin]); + assert_true(document.featurePolicy.allowsFeature('fullscreen')); + }, 'Test wildcard port policy works as expected'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html.sub.headers new file mode 100644 index 0000000000..b813167ee6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-port-wildcard.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https://{{domains[]}}:*") diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html new file mode 100644 index 0000000000..eab3e8f697 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script> + test(function() { + var wildcard_origin = 'https:'; + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + [wildcard_origin]); + assert_true(document.featurePolicy.allowsFeature('fullscreen')); + }, 'Test scheme only policy works as expected'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html.sub.headers new file mode 100644 index 0000000000..14d0868f2b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/permissions-policy-header-scheme-only.https.sub.html.sub.headers @@ -0,0 +1 @@ +Permissions-Policy: fullscreen=("https:") diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-default-permissions-policy.tentative.https.sub.html b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-default-permissions-policy.tentative.https.sub.html new file mode 100644 index 0000000000..4962b42721 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-default-permissions-policy.tentative.https.sub.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<title>Test that private state token redemption is enabled/disabled according to the permissions policy</title> + +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/permissions-policy/resources/permissions-policy.js></script> + <script> + 'use strict'; + const same_origin_src = '/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Default "private-state-token-redemption" permissions policy ["self"]'; + + test(() => { + try { + // The permissions policy gates redemption and signing via both the Fetch + // and XHR interfaces. + new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-redemption" + } + }); + new Request("https://destination.example/", { + privateToken: { + version: 1, + operation: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + } + }); + + const redemption_xhr = new XMLHttpRequest(); + redemption_xhr.open("GET", "https://issuer.example/"); + redemption_xhr.setPrivateToken({ + version: 1, + operation: "token-redemption" + }); + + const signing_xhr = new XMLHttpRequest(); + signing_xhr.open("GET", "https://destination.example/"); + signing_xhr.setPrivateToken({ + version: 1, + operation: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + }); + } catch (e) { + assert_unreached(); + } + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('Private state token redemption', t, same_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 4, desc); + }); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('Private state token redemption', t, cross_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }); + }, header + ' disallows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-supported-by-permissions-policy.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-supported-by-permissions-policy.tentative.html new file mode 100644 index 0000000000..0399f167fb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/private-state-token-redemption-supported-by-permissions-policy.tentative.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<title>Test that private state token redemption is advertised in the feature list</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + test(() => { + assert_in_array('private-state-token-redemption', document.featurePolicy.features()); + }, 'document.featurePolicy.features should advertise private-state-token-redemption.'); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/common.js b/testing/web-platform/tests/permissions-policy/experimental-features/resources/common.js new file mode 100644 index 0000000000..308f787da6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/common.js @@ -0,0 +1,94 @@ +const url_base = "/permissions-policy/experimental-features/resources/"; +window.messageResponseCallback = null; + +function setFeatureState(iframe, feature, origins) { + iframe.setAttribute("allow", `${feature} ${origins};`); +} + +// Returns a promise which is resolved when the <iframe> is navigated to |url| +// and "load" handler has been called. +function loadUrlInIframe(iframe, url) { + return new Promise((resolve) => { + iframe.addEventListener("load", resolve); + iframe.src = url; + }); +} + +// Posts |message| to |target| and resolves the promise with the response coming +// back from |target|. +function sendMessageAndGetResponse(target, message) { + return new Promise((resolve) => { + window.messageResponseCallback = resolve; + target.postMessage(message, "*"); + }); +} + + +function onMessage(e) { + if (window.messageResponseCallback) { + window.messageResponseCallback(e.data); + window.messageResponseCallback = null; + } +} + +window.addEventListener("message", onMessage); + +// Waits for |load_timeout| before resolving the promise. It will resolve the +// promise sooner if a message event with |e.data.id| of |id| is received. +// In such a case the response is the contents of the message |e.data.contents|. +// Otherwise, returns false (when timeout occurs). +function waitForMessageOrTimeout(t, id, load_timeout) { + return new Promise((resolve) => { + window.addEventListener( + "message", + (e) => { + if (!e.data || e.data.id !== id) + return; + resolve(e.data.contents); + } + ); + t.step_timeout(() => { resolve(false); }, load_timeout); + }); +} + +function createIframe(container, attributes) { + var new_iframe = document.createElement("iframe"); + for (attr_name in attributes) + new_iframe.setAttribute(attr_name, attributes[attr_name]); + container.appendChild(new_iframe); + return new_iframe; +} + +// Returns a promise which is resolved when |load| event is dispatched for |e|. +function wait_for_load(e) { + return new Promise((resolve) => { + e.addEventListener("load", resolve); + }); +} + +setup(() => { + window.reporting_observer_instance = new ReportingObserver((reports, observer) => { + if (window.reporting_observer_callback) { + reports.forEach(window.reporting_observer_callback); + } + }, {types: ["permissions-policy-violation"]}); + window.reporting_observer_instance.observe(); + window.reporting_observer_callback = null; +}); + +// Waits for a violation in |feature| and source file containing |file_name|. +function wait_for_violation_in_file(feature, file_name) { + return new Promise( (resolve) => { + assert_equals(null, window.reporting_observer_callback); + window.reporting_observer_callback = (report) => { + var feature_match = (feature === report.body.featureId); + var file_name_match = + !file_name || + (report.body.sourceFile.indexOf(file_name) !== -1); + if (feature_match && file_name_match) { + window.reporting_observer_callback = null; + resolve(report); + } + }; + }); +} diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html new file mode 100644 index 0000000000..3d5aab95ad --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html @@ -0,0 +1,47 @@ +<!doctype html> +<input autofocus onfocus="autofocus_onfocus()"/> +<script> + let autofocused = false; + function autofocus_onfocus() { + autofocused = true; + } + + /** + * @param target object: Target to call |focus()| with. + * @param timeout integer | undefined: Timeout to wait for the focus event. + * If unspecified, a timeout will not be set. + * @param focus_target boolean | undefined: Wether to focus the target after + * listening for |onfocus| event. + */ + function wait_focus_event(target, timeout, focus_target) { + return new Promise((resolve) => { + if (timeout) + setTimeout(() => resolve(false), timeout); + + target.onfocus = () => resolve(true); + if (focus_target) + target.focus(); + }); + } + + function post_result(destination, result) { + destination.postMessage({focused: result}, "*"); + } + + window.addEventListener("message", (e) => { + if (e.data.event === "autofocus") { + if (autofocused) + post_result(e.source, true); + + wait_focus_event(document.querySelector("input"), e.data.timeout) + .then(result => post_result(e.source, result)); + } else if (e.data.event === "focus-window") { + wait_focus_event(window, e.data.timeout, true /* focus_target */) + .then(result => post_result(e.source, result)); + } else if (e.data.event === "focus-input") { + const input_element = document.querySelector("input"); + wait_focus_event(input_element, e.data.timeout, true /* focus_target */) + .then(result => post_result(e.source, result)); + } +}); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload-contents.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload-contents.html new file mode 100644 index 0000000000..a6e98c24e6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload-contents.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<body> + <p>This page is lazyloaded.</p> + <script> + let query_index = window.location.href.indexOf("?id="); + let id = window.location.href.substr(query_index + 4); + window.addEventListener("load", () => { + let p = document.querySelector("p"); + let contents = p ? p.innerHTML : "null"; + window.parent.postMessage({"id": id, "contents": contents}, "*"); + }); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload.png b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload.png Binary files differnew file mode 100644 index 0000000000..fd3da53a29 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/lazyload.png diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html new file mode 100644 index 0000000000..7a055f0e7b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/permissions-policy-private-state-token-redemption.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<script> + 'use strict'; + + window.onload = function() { + // When the private-state-token-redemption permissions policy is enabled, redemption + // and signing ("send-redemption-record") should both be available; when it's disabled, + // they should both be unavailable. Send the number of available operations + // upstream in order to enforce this in assertions. + let num_enabled = 4; + try { + new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-redemption" + } + }); + } catch (e) { + num_enabled--; + } + try { + new Request("https://destination.example/", { + privateToken: { + version: 1, + operation: "send-redemption-record", + issuers: ["https://issuer.example/"] + } + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "token-redemption" + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://destination.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "send-redemption-record", + issuers: ["https://issuer.example/"] + }); + } catch (e) { + num_enabled--; + } + + parent.postMessage({ + type: 'availability-result', + num_operations_enabled: num_enabled, + }, '*'); + } +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/unload-helper.js b/testing/web-platform/tests/permissions-policy/experimental-features/resources/unload-helper.js new file mode 100644 index 0000000000..9739ead69d --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/unload-helper.js @@ -0,0 +1,41 @@ +// Code used by controlling frame of the unload policy tests. + +const MAIN_FRAME = 'main'; +const SUBFRAME = 'sub'; + +async function isUnloadAllowed(remoteContextWrapper) { + return remoteContextWrapper.executeScript(() => { + return document.featurePolicy.allowsFeature('unload'); + }); +} + +// Checks whether a frame runs unload handlers. +// This checks the policy directly and also installs an unload handler and +// navigates the frame checking that the handler ran. +async function assertWindowRunsUnload( + remoteContextWrapper, name, {shouldRunUnload}) { + const maybeNot = shouldRunUnload ? '' : 'not '; + assert_equals( + await isUnloadAllowed(remoteContextWrapper), shouldRunUnload, + `${name}: unload in ${name} should ${maybeNot}be allowed`); + + // Set up recording of whether unload handler ran. + await remoteContextWrapper.executeScript((name) => { + localStorage.setItem(name, 'did not run'); + addEventListener('unload', () => localStorage.setItem(name, 'did run')); + }, [name]); + + // Navigate away and then back. + const second = await remoteContextWrapper.navigateToNew(); + // Navigating back ensures that the unload has completed. + // Also if the navigation is cross-site then we have to return + // to the original origin in order to read the recorded unload. + second.historyBack(); + + // Check that unload handlers ran as expected. + const recordedUnload = await remoteContextWrapper.executeScript( + (name) => localStorage.getItem(name), [name]); + assert_equals( + recordedUnload, `did ${maybeNot}run`, + `${name}: unload should ${maybeNot}have run`); +} diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html new file mode 100644 index 0000000000..9f78ea4bc2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<style> + body, html { + height: 100%; + width: 100%; + } + #spacer { + width: 1500px; + height: 1500px; + background-color: red; + } +</style> +<body> + <p>This page is scrollable.</p> + <div id="spacer"></div> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html new file mode 100644 index 0000000000..fd432b33f6 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html @@ -0,0 +1,13 @@ +<!doctype html> +<title>Ref: vertical-scroll test for scrollbar</title> +<iframe src="/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html"></iframe> +<script> + let iframe = document.querySelector("iframe"); + let overflow_y = "visible"; + if (window.location.search.indexOf("no-vertical-scrollbar") !== -1) + overflow_y = "hidden" + iframe.addEventListener("load", () => { + iframe.contentDocument.body.style.overflowY = overflow_y; + }); +</script> + diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollintoview.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollintoview.html new file mode 100644 index 0000000000..7bed27c260 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-scrollintoview.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<style> +html, body, #container { + width: 100%; + height: 100%; +} + +#spacer { + width: 200%; + height: 200%; +} +</style> +<div id="container"> + <div id="spacer"></div> + <button>Element To Scroll</button> +</div> +<script> + window.addEventListener('message', onMessageReceived); + + function scrollingElementBounds() { + var rect = document.querySelector("button").getBoundingClientRect(); + return { + x: rect.x, y: rect.y, width: rect.width, height: rect.height + }; + } + + function onMessageReceived(e) { + if (!e.data || !e.data.type) + return; + switch(e.data.type) { + case "scroll": + document.querySelector("button").scrollIntoView({behavior: "instant"}); + ackMessage({bounds: scrollingElementBounds()}, e.source); + break; + + case "scrolling-element-bounds": + ackMessage({bounds: scrollingElementBounds()}, e.source); + break; + } + } + + function ackMessage(msg, source) { + source.postMessage(msg, "*"); + } +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-action.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-action.html new file mode 100644 index 0000000000..51b715f30a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-action.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<style> + body, html { + height: 100%; + width: 100%; + overflow: hidden; + } + body { + touch-action: none; + } +</style> +<body> + <p>This page blocks all 'touch-action'.</p> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-block.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-block.html new file mode 100644 index 0000000000..4c204055af --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-touch-block.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<style> +body, div, html { + height: 100%; + width: 100%; + overflow: hidden; +} +p { + margin-bottom: 1000px; + margin-right: 1000px; +} +</style> +<body> + <div id="blocker"> + <p>This page blocks 'touchstart' and 'touchmove'.</p> + </div> + <script> + function doSomethingUnimportant(e) { + return false !== e; + } + + function preventDefault(e) { + e.preventDefault(); + } + + document.addEventListener("touchstart", doSomethingUnimportant, {passive: false}); + document.addEventListener("touchmove", doSomethingUnimportant, {passive: false}); + + function messageHandler(e) { + let msg = e.data; + let element = document.querySelector(msg.query); + if (msg.prevent) { + element.addEventListener(msg.event_type, preventDefault, {passive: false}); + } else { + element.addEventListener(msg.event_type, doSomethingUnimportant); + } + e.source.postMessage(msg, "*"); + } + + window.addEventListener("message", messageHandler); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-wheel-block.html b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-wheel-block.html new file mode 100644 index 0000000000..21fc2b9b39 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll-wheel-block.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<style> + body, html { + height: 100%; + width: 100%; + overflow: hidden; + } +</style> +<body> + <p>This page blocks all 'mouse-wheel'.</p> +<script> + function defaultScroll() { + window.scrollTo(0, 0); + } + + document.body.addEventListener( + "wheel", + (e) => { e.preventDefault(); defaultScroll(); }, {passive: false}); + + defaultScroll(); +</script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll.js b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll.js new file mode 100644 index 0000000000..88835cc602 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/resources/vertical-scroll.js @@ -0,0 +1,25 @@ +function rectMaxY(rect) { + return rect.height + rect.y; +} + +function rectMaxX(rect) { + return rect.width + rect.x; +} + +function isEmptyRect(rect) { + return !rect.width || !rect.height; +} + +// Returns true if the given rectangles intersect. +function rects_intersect(rect1, rect2) { + if (isEmptyRect(rect1) || isEmptyRect(rect2)) + return false; + return rect1.x < rectMaxX(rect2) && + rect2.x < rectMaxX(rect1) && + rect1.y < rectMaxY(rect2) && + rect2.y < rectMaxY(rect1); +} + +function rectToString(rect) { + return `Location: (${rect.x}, ${rect.y}) Size: (${rect.width}, ${rect.height})`; +} diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js b/testing/web-platform/tests/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js new file mode 100644 index 0000000000..3fdc9ed047 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/unload-allowed-by-default.tentative.window.js @@ -0,0 +1,22 @@ +// META: title='unload' Policy : allowed by default +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/unload-helper.js +// META: timeout=long + +'use strict'; + +// Check that unload is allowed by policy in main frame and subframe by default. +promise_test(async t => { + const rcHelper = + new RemoteContextHelper({scripts: ['./resources/unload-helper.js']}); + // In the same browsing context group to ensure BFCache is not used. + const main = await rcHelper.addWindow(); + const sameOriginSubframe = await main.addIframe(); + const crossOriginSubframe = await main.addIframe({ origin: 'HTTP_REMOTE_ORIGIN' }); + await assertWindowRunsUnload(sameOriginSubframe, 'sameOriginSubframe', { shouldRunUnload: true }); + await assertWindowRunsUnload(crossOriginSubframe, 'crossOriginSubframe', { shouldRunUnload: true }); + await assertWindowRunsUnload(main, 'main', {shouldRunUnload: true}); +}); diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js new file mode 100644 index 0000000000..b2fb19ae12 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed-subframe.tentative.window.js @@ -0,0 +1,21 @@ +// META: title='unload' Policy : allowed in main frame but disallowed in subframe +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/unload-helper.js +// META: timeout=long + +'use strict'; + +// Check that unload is allowed by policy in main but can be disabled in the +// subframe. +promise_test(async t => { + const rcHelper = + new RemoteContextHelper({scripts: ['./resources/unload-helper.js']}); + // In the same browsing context group to ensure BFCache is not used. + const main = await rcHelper.addWindow(); + const subframe = + await main.addIframe({headers: [['Permissions-Policy', 'unload=()']]}); + await assertWindowRunsUnload(subframe, 'subframe', {shouldRunUnload: false}); + await assertWindowRunsUnload(main, 'main', {shouldRunUnload: true}); +}); diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed.tentative.window.js b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed.tentative.window.js new file mode 100644 index 0000000000..c93443c101 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/unload-disallowed.tentative.window.js @@ -0,0 +1,21 @@ +// META: title='unload' Policy : disallowed when header is () +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/utils.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/unload-helper.js +// META: timeout=long + +'use strict'; + +// Check that unload can be disabled by policy in main frame and subframe. +promise_test(async t => { + const rcHelper = + new RemoteContextHelper({scripts: ['./resources/unload-helper.js']}); + // In the same browsing context group to ensure BFCache is not used. + const main = await rcHelper.addWindow( + {headers: [['Permissions-Policy', 'unload=()']]}, + ); + const subframe = await main.addIframe(); + await assertWindowRunsUnload(subframe, 'subframe', {shouldRunUnload: false}); + await assertWindowRunsUnload(main, 'main', {shouldRunUnload: false}); +}); diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html new file mode 100644 index 0000000000..67cae05ee8 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html @@ -0,0 +1,113 @@ +<!doctype html> +<meta name="timeout" content="long"> +<title>vertical-scroll test for touch-action</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> + +<script> + "use strict"; + + let url = url_base + "vertical-scroll-scrollable-content.html"; + let iframeElement = document.querySelector("iframe"); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + // Sanity-check: Verify we can scroll using the test-API (empty <iframe>). + promise_test(async() => { + window.scrollTo(0, 0); + + await inject_input("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + await inject_input("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + }, "Verify that the page normally scrolls."); + + // Sanity-check: <iframe> normally scrolls. + promise_test(async() => { + // Make sure <window> can scroll both towards right and bottom. + window.scrollTo(0, 0); + + await loadUrlInIframe(iframeElement, url); + iframeElement.contentWindow.scrollTo(0, 0); + + await inject_input("down"); + assert_greater_than( + iframeElement.contentWindow.scrollY, + 0, + "<iframe> must have scrolled down."); + + + // Apply the scroll gesture. + await inject_input("right"); + assert_greater_than( + iframeElement.contentWindow.scrollX, + 0, + "<iframe> must have scrolled right."); + + }, "Verify that the <iframe> normally scrolls."); + + // Disable 'vertical-scroll': <iframe> should only scroll horizontally. + promise_test(async() => { + window.scrollTo(0, 0); + + // Disallow vertical scroll and reload the <iframe>. + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + iframeElement.contentWindow.scrollTo(0, 0); + + // Apply the scroll gesture. Main frame should and <iframe> should not + // scroll vertically. + await inject_input("down"); + assert_equals(iframeElement.contentWindow.scrollY, + 0, + "<iframe> must not scroll vertically."); + assert_greater_than(window.scrollY, + 0, + "Page must scroll vertically."); + + window.scrollTo(0, 0); + iframeElement.contentWindow.scrollTo(0, 0); + + await inject_input("right"); + assert_greater_than(iframeElement.contentWindow.scrollX, + 0, + "<iframe> must have scrolled right."); + }, "When 'vertical-scroll' is disabled in a document, scrollable contents " + + "can only *horizontally* scroll."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html new file mode 100644 index 0000000000..6522254076 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html @@ -0,0 +1,4 @@ +<!doctype html> +<title>vertical-scroll test for vertical scrollbar</title> +<link rel="match" href="/permissions-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html?no-vertical-scrollbar"> +<iframe src="/permissions-policy/experimental-features/resources/vertical-scroll-scrollable-content.html" allow="vertical-scroll 'none'"></iframe> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html new file mode 100644 index 0000000000..cda6c49abb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<title>Ensure 'vertical-scroll' does not affect main frame</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} +.spacer { + width: 100%; + height: 100%; + margin-top: 100%; + margin-bottom: 100%; +} +</style> +<p> Making sure there is room for vertical scroll </p> +<div class="spacer"></div> +<div class="spacer"></div> +</p>EOP</p> +<script> + "use strict"; + + // Sanity check. + test(() => { + assert_false(document.featurePolicy.allowsFeature("vertical-scroll"), + "Expected 'vertical-scroll' to be disabled."); + }, "'vertical-scroll' disabled in main document."); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + promise_test(async() => { + window.scrollTo(0, 0); + await inject_input("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + }, "Verify that the page scrolls vertically."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers new file mode 100644 index 0000000000..8d9c01e148 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers @@ -0,0 +1 @@ +Permissions-Policy: vertical-scroll=() diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html new file mode 100644 index 0000000000..4f78dd914a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html @@ -0,0 +1,117 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 95%; + height: 95%; + overflow: scroll; + margin-top: 200%; +} + +.spacer { + width: 100%; + height: 100%; + margin-top: 100%; + margin-bottom: 100%; +} + +</style> +<p> An <iframe> further below which is not allowed to block scroll.</p> +<div class="spacer"></div> +<iframe></iframe> +<p> Making sure there is room for vertical scroll </p> +<script> + "use strict"; + + let url = url_base + "vertical-scroll-scrollintoview.html"; + let iframeElement = document.querySelector("iframe"); + + function iframeBounds() { + return iframeElement.getBoundingClientRect(); + } + + // Enabled 'vertical-scroll': scrollIntoView should work in all frames. + promise_test(async() => { + window.scrollTo(0, 0); + await loadUrlInIframe(iframeElement, url); + + await sendMessageAndGetResponse( + iframeElement.contentWindow, + {type: "scrolling-element-bounds"}).then((response) => { + let iframeBoundsAtOrigin = { + x: 0, + y: 0, + width: iframeBounds().width, + height: iframeBounds().height}; + let scrollingElementBounds = response.bounds; + assert_false( + rects_intersect(iframeBoundsAtOrigin, scrollingElementBounds), + "Scrolling element should not be visible in <iframe>." + + `Scrolling element's bounds is: ${rectToString(response.bounds)} ` + + "but <iframe>'s size is:" + + `${iframeBounds().width}x${iframeBounds().height}.`); + }); + + // Scroll the scrolling element inside the <iframe>. + await sendMessageAndGetResponse(iframeElement.contentWindow, + {type: "scroll"}); + // The page should have scrolled vertically. + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + }, "Calling 'scrollIntoView()' inside a <iframe> will propagate up by" + + " default('vertical-scroll' enabled)."); + + // Disabled 'vertical-scroll': The scope of scrollIntoView is within the set + // of disabled frames (does not propagate to main frame). + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + + await sendMessageAndGetResponse( + iframeElement.contentWindow, + {type: "scrolling-element-bounds"}).then((response) => { + let iframeBoundsAtOrigin = { + x: 0, + y: 0, + width: iframeBounds().width, + height: iframeBounds().height}; + let scrollingElementBounds = response.bounds; + assert_false(rects_intersect(iframeBoundsAtOrigin, scrollingElementBounds), + "Scrolling element should not be visible in <iframe>." + + `Scrolling element's bounds is: ${rectToString(response.bounds)}` + + "but <iframe>'s size is:" + + `${iframeBounds().width}x${iframeBounds().height}.`); + }); + + // Scroll scrolling element inside the <iframe>. + await sendMessageAndGetResponse(iframeElement.contentWindow, + {type: "scroll"}).then((response) => { + // Make sure the nested <iframe> is visible. + let scrollingElementBounds = response.bounds; + let iframeBoundsAtOrigin = { + x: 0, + y: 0, + width: iframeBounds().width, + height: iframeBounds().height}; + // The scrolling element should be visible inside <iframe>. + assert_true(rects_intersect(iframeBoundsAtOrigin, scrollingElementBounds), + "Scrolling element should be visible in <iframe>." + + `Scrolling element's bounds is: ${rectToString(response.bounds)}` + + "but <iframe>'s size is:" + + `${iframeBounds().width}, ${iframeBounds().height}.`); + // The page however should not have scrolled. + assert_equals(window.scrollY, 0, "Main frame must not scroll vertically."); + }); + }, "Calling 'scrollIntoView()' inside a <iframe> with" + + " 'vertical-scroll none;'will not propagate upwards."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html new file mode 100644 index 0000000000..8c2eec93b7 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html @@ -0,0 +1,103 @@ +<!doctype html> +<meta name="timeout" content="long"> +<title>vertical-scroll test for touch-action</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> + +<script> + "use strict"; + + let url = url_base + "vertical-scroll-touch-action.html"; + let iframeElement = document.querySelector("iframe"); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + // Sanity-check: Verify we can scroll using the test-API (empty <iframe>). + promise_test(async() => { + window.scrollTo(0, 0); + + await inject_input("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + await inject_input("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + }, "Verify that the page normally scrolls."); + + // Enable 'vertical-scroll': "touch-action" should be able to block scroll. + promise_test(async() => { + // Make sure <window> can scroll both towards right and bottom. + window.scrollTo(0, 0); + + await loadUrlInIframe(iframeElement, url); + + // Apply the scroll gesture. + await inject_input("down"); + + // The scroll should normally bubble and affect this page, but the <iframe> + // is allowed to block it. + assert_equals(window.scrollY, + 0, + "Main frame must not scroll vertically"); + }, "Vertical scrolling through 'touch-action' can normally be blocked if" + + " 'pan-y' is not present."); + + // Disable 'vertical-scroll': "touch-action" should not be able to block + // scroll. + promise_test(async() => { + window.scrollTo(0, 0); + + // Disallow vertical scroll and reload the <iframe>. + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + + // Apply the scroll gesture. Main frame should scroll vertically. + await inject_input("down"); + + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + window.scrollTo(0, 0); + + await inject_input("right"); + assert_equals( + window.scrollX, + 0, + "'pan-x' can be blocked even when 'vertical-scroll' is disabled"); + }, "Vertical scrolling (and only vertical scrolling) through 'touch-action'" + + " cannot be blocked by a disabled frame."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html new file mode 100644 index 0000000000..341e543934 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html @@ -0,0 +1,237 @@ +<!doctype html> +<title>vertical-scroll test for touch-action</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> + +<script> + "use strict"; + + let url = url_base + "vertical-scroll-touch-block.html"; + let iframeElement = document.querySelector("iframe"); + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise( (r) => { + window.resolve_on_input_api_ready = r; + }); + + }, "Make sure input injection API is ready."); + + // Sanity-check: Verify we can scroll using the test-API. The <iframe> with no + // query arguments would not preventDefault() any events. + promise_test(async() => { + window.scrollTo(0, 0); + await loadUrlInIframe(iframeElement, url); + + await inject_scroll("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + await inject_scroll("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + }, "Verify that the page normally scrolls."); + + // Enable 'vertical-scroll': Blocking "touchstart" can block scrolling. + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "*"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + + await inject_scroll("down"); + assert_equals(window.scrollY, + 0, + "Main frame must not scroll vertically."); + }, "Calling 'preventDefault' on 'touchstart' blocks vertical scroll when " + + " the feature is enabled."); + + // Disable 'vertical-scroll': Blocking "touchstart" cannot block vertical + // scroll. + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + + await inject_scroll("down"); + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + + // Horizontal scrolling must still be blocked. + window.scrollTo(0, 0); + await inject_scroll("right"); + assert_equals(window.scrollX, + 0, + "Main frame must not scroll horizontally."); + }, "When feature is disabled, calling 'preventDefault' on 'touchstart' does" + + " not block vertical scroll, but still bocks horizontal scroll."); + + // Enable 'vertical-scroll': Blocking "touchmove" can block scrolling. + promise_test(async() => { + // Make sure <window> can scroll both towards right and bottom. + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "*"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchmove", + query: "#blocker", + prevent: true + }); + + await inject_scroll("down"); + assert_equals(window.scrollY, + 0, + "Main frame must not scroll vertically"); + }, "Calling 'preventDefault' on 'touchmove' blocks vertical scroll when " + + "the feature is enabled."); + + // Disable 'vertical-scroll': Blocking "touchmove" cannot block vertical + // scroll. + promise_test(async() => { + window.scrollTo(0, 0); + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchmove", + query: "#blocker", + prevent: true + }); + await new Promise((r) => { + window.setTimeout(r, 100); + }); + + await inject_scroll("down"); + assert_greater_than(window.scrollY, + 0, + "Main frame must scroll vertically."); + }, "When feature is disabled, calling 'preventDefault' on 'touchmove' does" + + " not block vertical scroll."); + + // Disable 'vertical-scroll': Add a non-preventing handler, and then a + // preventing handler and verify vertical scroll is possible and horizontal + // is blocked. + promise_test(async() => { + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + + // Add a non-blocking handler. + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: false + }); + + window.scrollTo(0, 0); + await inject_scroll("down"); + assert_greater_than(window.scrollY, 0, "Page must have scrolled down."); + + window.scrollTo(0, 0); + await inject_scroll("right"); + assert_greater_than(window.scrollX, 0, "Page must have scrolled right."); + + // Add a blocking handler. + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + window.scrollTo(0, 0); + await inject_scroll("down"); + assert_greater_than(window.scrollY, + 0, + "Page must have scrolled down (preventing handler)."); + + window.scrollTo(0, 0); + await inject_scroll("right"); + assert_equals(window.scrollX, + 0, + "Page must not have scrolled horizontally."); + }, "Verify that starting with a non-preventing listener and then switching" + + " to a preventing one works as intended."); + + // Disable 'vertical-scroll': A preventing 'touchstart' handler does not allow + // other multi-touch actions such as 'pinch-zoom'. + promise_test(async() => { + assert_true(!!window.visualViewport, + "'visualViewport' is needed for this test."); + + setFeatureState(iframeElement, "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement, url); + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: false + }); + + // Sanity check: Pinch zoom in should work since 'touchstart' is not + // preventing. + let current_scale_factor = window.visualViewport.scale; + await inject_zoom("in"); + let new_scale_factor = window.visualViewport.scale; + assert_greater_than( + new_scale_factor, + current_scale_factor, + "Pinch zoom should work since 'touchstart' is not prevented."); + + // Add a preventing handler and verify pinch zoom is now blocked. + await sendMessageAndGetResponse(iframeElement.contentWindow, + { + event_type: "touchstart", + query: "#blocker", + prevent: true + }); + current_scale_factor = new_scale_factor; + await inject_zoom("out"); + new_scale_factor = window.visualViewport.scale; + assert_equals( + new_scale_factor, + current_scale_factor, + "Pinch zoom must be blocked."); + }, "Verify that pinch zoom is blocked in frames with 'vertical-scroll' none'" + + " where 'touchstart' is prevent defaulted."); +</script> diff --git a/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html new file mode 100644 index 0000000000..2627fd4154 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html @@ -0,0 +1,145 @@ +<!doctype html> +<title>vertical-scroll test for 'mousewheel'</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/permissions-policy/experimental-features/resources/common.js"></script> +<script src="/permissions-policy/experimental-features/resources/vertical-scroll.js"></script> +<style> +html, body { + height: 100%; + width: 100%; +} + +iframe { + width: 90%; + height: 90%; + margin: 0; + padding: 0; +} + +.spacer { + width: 100%; + height: 100%; + margin: 100%; +} + +</style> +<iframe></iframe> +<br/> +<p>Spacers below to make page scrollable</p> +<br/> +<div class="spacer"></div> +<div class="spacer"></div> +<p> EOF </p> +<script> + "use strict"; + + let url = url_base + "vertical-scroll-wheel-block.html"; + function iframeElement() { + return document.querySelector("iframe"); + } + + // Used as the polling interval when waiting for a specific condition. + let verify_scroll_offset_delay = 5; + let no_scroll_timout = 50; + + function waitUntilSatisfied(testInstance, predicate) { + return new Promise((r) => { + function testPredicate() { + if (predicate()) { + r(); + } else { + testInstance.step_timeout(testPredicate, verify_scroll_offset_delay); + } + } + testPredicate(); + }); + } + + function resetScroll(testInstance) { + window.scrollTo({top: 0, left: 0, behavior: "instant"}); + return waitUntilSatisfied(testInstance, () => { + return window.scrollX === 0 && window.scrollY === 0; + }); + } + + function waitForMinimumScrollOffset(testInstance, minX, minY) { + return waitUntilSatisfied(testInstance, () => { + return window.scrollX >= minX && window.scrollY >= minY; + }); + } + + function waitFor(testInstance, delay) { + let checked_once = false; + return waitUntilSatisfied(testInstance, () => { + if (checked_once) + return true; + checked_once = true; + return false; + }); + } + + // Wait for the helper scripts to load. + promise_test(async() => { + if (window.input_api_ready) + return Promise.resolve(); + await new Promise((r) => { + window.resolve_on_input_api_ready = r; + }); + }, "Make sure input injection API is ready."); + + // Sanity-check: Test API for scrolling along y-axis works as expected. + promise_test(async(testInstance) => { + await resetScroll(testInstance); + await inject_wheel_scroll("down"); + await waitForMinimumScrollOffset(testInstance, 0, 1); + assert_greater_than(window.scrollY, 0, "Expected vertical scroll."); + }, "Sanity-check: the page scrolls vertically in response to vertical wheel."); + + // Sanity-check: Test API for scrolling along x-axis works as expected. + promise_test(async(testInstance) => { + await resetScroll(testInstance); + await inject_wheel_scroll("right"); + await waitForMinimumScrollOffset(testInstance, 1, 0); + assert_greater_than(window.scrollX, 0, "Expected horizontal scroll."); + }, "Sanity-check: the page scrolls horizontally in response to horizontal wheel."); + + // Test that when 'vertical-scroll' is enabled, vertical scroll can be + // blocked by canceling 'wheel' event. + promise_test(async(testInstance) => { + setFeatureState(iframeElement(), "vertical-scroll", "*"); + await loadUrlInIframe(iframeElement(), url); + + await resetScroll(testInstance); + await inject_wheel_scroll("down") + await waitFor(testInstance, no_scroll_timout); + assert_equals(window.scrollY, 0, "Did not expected vertical scroll."); + }, "When 'vertical-scroll' is enabled canceling vertical 'wheel' " + + "blocks vertical scrolling."); + + // Test that when 'vertical-scroll' is disabled, vertical scroll cannot + // be blocked by canceling 'wheel' event. + promise_test(async(testInstance) => { + setFeatureState(iframeElement(), "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement(), url); + + await resetScroll(testInstance); + await inject_wheel_scroll("down"); + await waitForMinimumScrollOffset(testInstance, 0, 1); + assert_greater_than(window.scrollY, 0, "Expected vertical scroll."); + }, "When 'vertical-scroll' is disabled canceling vertical 'wheel' " + + "does not block vertical scrolling."); + + // Test that when 'vertical-scroll' is disabled, horizontal scroll can be + // blocked by canceling 'wheel' event. + promise_test(async(testInstance) => { + setFeatureState(iframeElement(), "vertical-scroll", "'none'"); + await loadUrlInIframe(iframeElement(), url); + + await resetScroll(testInstance); + await inject_wheel_scroll("right"); + await waitFor(testInstance, no_scroll_timout); + assert_equals(window.scrollX, 0, "Did not expect horizontal scroll."); + }, "When 'vertical-scroll' is disabled canceling horizontal 'wheel' " + + "blocks horizontal scrolling."); +</script> 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..b95deed7ce --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html @@ -0,0 +1,28 @@ +<!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.'); + + </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..da5fe80f66 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-default-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=/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.'); + </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..cc358a1c0f --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/payment-disabled-by-permissions-policy.https.sub.html @@ -0,0 +1,33 @@ +<!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.'); + </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..ef36bf97f1 --- /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'; + + promise_test(t => { + return 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); + + promise_test(t => { + return 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..563b7a80eb --- /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].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 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..4515679c18 --- /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].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(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src1 + pipe_front + header_policies[j].replace(")", "\\)") + 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..8d5c3f43f4 --- /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].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-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..9d98d1abcc --- /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://*.example.com", "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..648e3ac75e --- /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[]}}:{{ports[https][0]}}" "https://example.*.{{domains[]}}:{{ports[https][0]}}" "https://*.example.com" 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/private-state-token-issue-allowed-by-permissions-policy-attribute.tentative.https.sub.html b/testing/web-platform/tests/permissions-policy/private-state-token-issue-allowed-by-permissions-policy-attribute.tentative.https.sub.html new file mode 100644 index 0000000000..699b11cfef --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/private-state-token-issue-allowed-by-permissions-policy-attribute.tentative.https.sub.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Private State Tokens API Issuance operation container policy test</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'; + var same_origin_src = '/permissions-policy/resources/permissions-policy-private-state-token-issuance.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + + async_test(t => { + test_feature_availability( + 'Private State Token issuance request', t, same_origin_src, + (data, desc) => {assert_equals(data.num_operations_enabled, 2, desc);}, + 'private-state-token-issuance'); + }, 'Permissions policy "private-state-token-issuance" can be enabled \ +in same-origin iframe using allow="private-state-token-issuance" attribute'); + + async_test(t => { + test_feature_availability( + 'Private State Token issuance request', t, cross_origin_src, + (data, desc) => {assert_equals(data.num_operations_enabled, 2, desc);}, + 'private-state-token-issuance'); + }, 'Permissions policy "private-state-token-issuance" can be enabled \ +in cross-origin iframe using allow="private-state-token-issuance" attribute'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/private-state-token-issue-disabled-by-permissions-policy.tentative.https.sub.html b/testing/web-platform/tests/permissions-policy/private-state-token-issue-disabled-by-permissions-policy.tentative.https.sub.html new file mode 100644 index 0000000000..0c72b3da88 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/private-state-token-issue-disabled-by-permissions-policy.tentative.https.sub.html @@ -0,0 +1,67 @@ +<!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-private-state-token-issuance.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var test_desc_begin = 'Permissions policy header "private-state-token-issuance=()"'; + + test(() => { + + assert_throws_dom('NotAllowedError', () => { + const issue_request = new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-request" + } + }); + }); + + assert_throws_dom('NotAllowedError', () => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "token-request" + }); + }); + + }, test_desc_begin + ' disallows the top-level document.'); + + async_test(t => { + test_feature_availability('Private State Token issuance request', t, + same_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }); + }, test_desc_begin + ' disallows same-origin iframes.'); + + async_test(t => { + test_feature_availability('Private State Token issuance request', t, + cross_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }); + }, test_desc_begin + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'Private State Token issuance request', t, same_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }, 'private-state-token-issuance'); + }, test_desc_begin + ' and allow="private-state-token-issuance" disallows same-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'Private State Token issuance request', t, cross_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }, 'private-state-token-issuance'); + }, test_desc_begin + ' and allow="private-state-token-issuance" disallows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/private-state-token-issue-disabled-by-permissions-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/private-state-token-issue-disabled-by-permissions-policy.tentative.https.sub.html.headers new file mode 100644 index 0000000000..18cbd50fca --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/private-state-token-issue-disabled-by-permissions-policy.tentative.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: private-state-token-issuance=() diff --git a/testing/web-platform/tests/permissions-policy/private-state-token-issue-enabled-by-permissions-policy.tentative.https.sub.html b/testing/web-platform/tests/permissions-policy/private-state-token-issue-enabled-by-permissions-policy.tentative.https.sub.html new file mode 100644 index 0000000000..4b446f98d7 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/private-state-token-issue-enabled-by-permissions-policy.tentative.https.sub.html @@ -0,0 +1,58 @@ +<!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-private-state-token-issuance.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var test_desc_begin = 'Permissions policy header "private-state-token-issuance=*"'; + + test(() => { + try { + new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-request" + } + }); + } catch(e) { + assert_unreached(); + } + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "token-request" + }); + } catch(e) { + assert_unreached(); + } + + }, test_desc_begin + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('Private State Token issuance request', t, + same_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 2, desc);}); + }, test_desc_begin + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('Private State Token issuance request', t, + cross_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc);}); + }, test_desc_begin + ' disallows cross-origin iframes.'); + + async_test(t => { + test_feature_availability( + 'Private State Token issuance request', t, cross_origin_src, + (data, desc) => {assert_equals(data.num_operations_enabled, 2, desc);}, + 'private-state-token-issuance'); + }, test_desc_begin + ' and allow="private-state-token-issuance" allows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/permissions-policy/private-state-token-issue-enabled-by-permissions-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/permissions-policy/private-state-token-issue-enabled-by-permissions-policy.tentative.https.sub.html.headers new file mode 100644 index 0000000000..cef93ce872 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/private-state-token-issue-enabled-by-permissions-policy.tentative.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: private-state-token-issuance=* diff --git a/testing/web-platform/tests/permissions-policy/private-state-token-issue-supported-by-permissions-policy.tentative.html b/testing/web-platform/tests/permissions-policy/private-state-token-issue-supported-by-permissions-policy.tentative.html new file mode 100644 index 0000000000..52a392de57 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/private-state-token-issue-supported-by-permissions-policy.tentative.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + test(() => { + assert_in_array('private-state-token-issuance', document.featurePolicy.features()); + }, 'document.featurePolicy.features should advertise private-state-token-issuance.'); +</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/report-only-and-enforce.https.sub.html b/testing/web-platform/tests/permissions-policy/reporting/report-only-and-enforce.https.sub.html new file mode 100644 index 0000000000..6254b3247e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-only-and-enforce.https.sub.html @@ -0,0 +1,36 @@ +<!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> + <script src='/reporting/resources/report-helper.js'></script> + </head> + <body> + <script> + const base_url = `${location.protocol}//${location.host}`; + const endpoint = `${base_url}/reporting/resources/report.py`; + const enforcing_id = '69c90b97-8b7c-4439-b5d2-ff03f237b2b2'; + const report_only_id = '9d095a14-6b1c-40e1-be19-55a1fec97d11'; + + promise_test(async t => { + await setMediaPermission("granted", ["camera","microphone"]); + await promise_rejects_dom( + t, "NotAllowedError", navigator.mediaDevices.getUserMedia({video: true}), + "UserMedia camera access should not be allowed in this document."); + while(true) { + await wait(100); + const reports = await pollReports(endpoint, enforcing_id); + if (reports.length) { + checkReportExists(reports, 'permissions-policy-violation', location.href); + break; + } + } + const report_only_reports = await pollReports(endpoint, report_only_id); + assert_equals(report_only_reports.length, 0, "Report-only endpoint should not receive report."); + }, "Enforcing policy receives reports when both enforcing and report-only policies are set."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-only-and-enforce.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/reporting/report-only-and-enforce.https.sub.html.sub.headers new file mode 100644 index 0000000000..48f483dd45 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-only-and-enforce.https.sub.html.sub.headers @@ -0,0 +1,3 @@ +Permissions-Policy: camera=();report-to=enforcing-endpoint +Permissions-Policy-Report-Only: camera=();report-to=report-only-endpoint +Reporting-Endpoints: enforcing-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=69c90b97-8b7c-4439-b5d2-ff03f237b2b2", report-only-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=9d095a14-6b1c-40e1-be19-55a1fec97d11" diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-only-single-endpoint.https.sub.html b/testing/web-platform/tests/permissions-policy/reporting/report-only-single-endpoint.https.sub.html new file mode 100644 index 0000000000..7f9bad40cb --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-only-single-endpoint.https.sub.html @@ -0,0 +1,32 @@ +<!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> + <script src='/reporting/resources/report-helper.js'></script> + </head> + <body> + <script> + const base_url = `${location.protocol}//${location.host}`; + const endpoint = `${base_url}/reporting/resources/report.py`; + const id = 'c3841a1a-e750-4801-a50b-c4222d386515'; + + promise_test(async t => { + await setMediaPermission("granted", ["camera","microphone"]); + // This should not throw, as the enforcing policy does not block it. + await navigator.mediaDevices.getUserMedia({video: true}); + while(true) { + await wait(100); + const reports = await pollReports(endpoint, id); + if (reports.length) { + checkReportExists(reports, 'permissions-policy-violation', location.href); + break; + } + } + }, "Reporting-Endpoints defined endpoint received reports in report-only mode."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-only-single-endpoint.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/reporting/report-only-single-endpoint.https.sub.html.sub.headers new file mode 100644 index 0000000000..881eaba149 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-only-single-endpoint.https.sub.html.sub.headers @@ -0,0 +1,2 @@ +Permissions-Policy-Report-Only: camera=();report-to=camera-endpoint +Reporting-Endpoints: camera-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=c3841a1a-e750-4801-a50b-c4222d386515" diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-to-multiple-endpoints.https.sub.html b/testing/web-platform/tests/permissions-policy/reporting/report-to-multiple-endpoints.https.sub.html new file mode 100644 index 0000000000..ce392ed941 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-to-multiple-endpoints.https.sub.html @@ -0,0 +1,45 @@ +<!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> + <script src='/reporting/resources/report-helper.js'></script> + </head> + <body> + <script> + const base_url = `${location.protocol}//${location.host}`; + const endpoint = `${base_url}/reporting/resources/report.py`; + const camera_id = '6e0f343f-0a7a-4fee-82d9-00eb64531c26'; + const microphone_id = 'c63eecea-490b-4c38-8c4b-d72ecccd4e31'; + + promise_test(async t => { + await setMediaPermission("granted", ["camera","microphone"]); + await promise_rejects_dom( + t, "NotAllowedError", navigator.mediaDevices.getUserMedia({video: true}), + "UserMedia camera access should not be allowed in this document."); + await promise_rejects_dom( + t, "NotAllowedError", navigator.mediaDevices.getUserMedia({audio: true}), + "UserMedia microphone access should not be allowed in this document."); + + let camera_reports=[], microphone_reports=[]; + while(true) { + await wait(100); + if (!camera_reports.length) { + camera_reports = await pollReports(endpoint, camera_id); + } + if (!microphone_reports.length) { + microphone_reports = await pollReports(endpoint, microphone_id); + } + if (camera_reports.length && microphone_reports.length) { + checkReportExists(camera_reports, 'permissions-policy-violation', location.href); + checkReportExists(microphone_reports, 'permissions-policy-violation', location.href); + break; + } + } + }, "Reporting-Endpoints defined endpoint received reports."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-to-multiple-endpoints.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/reporting/report-to-multiple-endpoints.https.sub.html.sub.headers new file mode 100644 index 0000000000..14b742c558 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-to-multiple-endpoints.https.sub.html.sub.headers @@ -0,0 +1,2 @@ +Permissions-Policy: camera=();report-to=camera-endpoint, microphone=();report-to=microphone-endpoint +Reporting-Endpoints: camera-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=6e0f343f-0a7a-4fee-82d9-00eb64531c26", microphone-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=c63eecea-490b-4c38-8c4b-d72ecccd4e31" diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-to-single-endpoint.https.sub.html b/testing/web-platform/tests/permissions-policy/reporting/report-to-single-endpoint.https.sub.html new file mode 100644 index 0000000000..bcedd242d2 --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-to-single-endpoint.https.sub.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> + <script src='/reporting/resources/report-helper.js'></script> + </head> + <body> + <script> + const base_url = `${location.protocol}//${location.host}`; + const endpoint = `${base_url}/reporting/resources/report.py`; + const id = 'c898489f-00d2-40af-a480-4e8d9f907469'; + + promise_test(async t => { + await setMediaPermission("granted", ["camera","microphone"]); + await promise_rejects_dom( + t, "NotAllowedError", navigator.mediaDevices.getUserMedia({video: true}), + "UserMedia camera access should not be allowed in this document."); + while(true) { + await wait(100); + const reports = await pollReports(endpoint, id); + if (reports.length) { + checkReportExists(reports, 'permissions-policy-violation', location.href); + break; + } + } + }, "Reporting-Endpoints defined endpoint received reports."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/permissions-policy/reporting/report-to-single-endpoint.https.sub.html.sub.headers b/testing/web-platform/tests/permissions-policy/reporting/report-to-single-endpoint.https.sub.html.sub.headers new file mode 100644 index 0000000000..bbfe7e8afa --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/reporting/report-to-single-endpoint.https.sub.html.sub.headers @@ -0,0 +1,2 @@ +Permissions-Policy: camera=();report-to=camera-endpoint +Reporting-Endpoints: camera-endpoint="https://{{host}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=c898489f-00d2-40af-a480-4e8d9f907469" 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-private-state-token-issuance.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-private-state-token-issuance.html new file mode 100644 index 0000000000..f2bf31918e --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-private-state-token-issuance.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<script> +'use strict'; + +window.onload = function() { + // Check issuance operation availability for both Request and XMLHttpRequest. + // They are tied to the same permission policy. They should be both enabled or disabled. + let num_enabled = 2; + try { + const issue_request = new Request("https://issuer.example/", { + privateToken: { + version: 1, + operation: "token-request" + } + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setPrivateToken({ + version: 1, + operation: "token-request" + }); + } catch (e) { + num_enabled--; + } + parent.postMessage({ + type: 'availability-result', + num_operations_enabled: num_enabled, + }, '*'); +} +</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..5c2562946b --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.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: 'screen-wake-lock' }, 'granted'); + + try { + const wakeLock = await navigator.wakeLock.request("screen"); + window.parent.postMessage({ type: 'availability-result', enabled: true }, "*"); + await wakeLock.release(); + } catch (e) { + if (e instanceof DOMException && e.name === "NotAllowedError") { + window.parent.postMessage({ type: 'availability-result', enabled: false }, "*"); + } else { + throw e; + } + } +}); +</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..32fb4cfd4a --- /dev/null +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js @@ -0,0 +1,472 @@ +// 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 +// by passing "allowfullscreen". +// is_promise_test: Optional argument, true if this call should return a +// promise. Used by test_feature_availability_with_post_message_result() +function test_feature_availability( + feature_description, test, src, expect_feature_available, feature_name, + allow_attribute, is_promise_test = false) { + 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); + } + + function expectFeatureAvailable(evt) { + if (evt.source === frame.contentWindow && + evt.data.type === 'availability-result') { + expect_feature_available(evt.data, feature_description); + document.body.removeChild(frame); + test.done(); + } + } + + if (!is_promise_test) { + window.addEventListener('message', test.step_func(expectFeatureAvailable)); + document.body.appendChild(frame); + return; + } + + const promise = new Promise((resolve) => { + window.addEventListener('message', resolve); + }).then(expectFeatureAvailable); + document.body.appendChild(frame); + return promise; +} + +// 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 + '.'); + }; + return test_feature_availability( + null, test, src, test_result, allow_attribute, undefined, true); +} + +// 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); + promise_test( + t => { + return 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); + promise_test( + t => { + return 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. + promise_test( + t => { + return 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'. + promise_test( + t => { + return 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); + promise_test( + t => { + return 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); + promise_test( + t => { + return 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'. + promise_test( + t => { + return 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'. + promise_test( + t => { + return 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 |