diff options
Diffstat (limited to 'testing/web-platform/tests/feature-policy')
123 files changed, 4476 insertions, 0 deletions
diff --git a/testing/web-platform/tests/feature-policy/META.yml b/testing/web-platform/tests/feature-policy/META.yml new file mode 100644 index 0000000000..5fb6249c67 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/META.yml @@ -0,0 +1,4 @@ +spec: https://wicg.github.io/feature-policy/ +suggested_reviewers: + - bakulf + - clelland diff --git a/testing/web-platform/tests/feature-policy/README.md b/testing/web-platform/tests/feature-policy/README.md new file mode 100644 index 0000000000..646fc1e0be --- /dev/null +++ b/testing/web-platform/tests/feature-policy/README.md @@ -0,0 +1,59 @@ +# Feature Policy Guide +## How to Test a New Feature with Feature Policy + +This directory contains a framework to test features with feature policy. + +When adding a new feature to feature policy, the following cases should be tested: +* feature enabled by header policy [HTTP tests] + + test when feature is enabled by feature policy HTTP header; +* feature disabled by header policy [HTTP tests] + + test when feature is disabled by feature policy HTTP header; +* feature enabled on self origin by header policy [HTTP tests] + + test when feature is enabled only on self origin by feature 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 +`/feature-policy/resources/featurepolicy.js`. Please refer to the comments +in `/feature-policy/resources/featurepolicy.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-feature-policy.https.sub.html.headers`. Example: + + Feature-Policy: feature-name * + + +* In `<feature-name>-<enabled | disabled | enabled-on-self-origin>-by-feature-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: +`/feature-policy/payment-disabled-by-feature-policy.https.sub.html` +`/feature-policy/payment-disabled-by-feature-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: +`/feature-policy/payment-allowed-by-feature-policy-attribute.https.sub.html` + +### How to Write Container Policy Tests with Redirect +Similar to the section above, append +`/feature-policy/resources/redirect-on-load.html#` to the argument `src` +passed to `test_feature_availability()`. + +Example: +`/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html` + diff --git a/testing/web-platform/tests/feature-policy/experimental-features/focus-without-user-activation-disabled-tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/focus-without-user-activation-disabled-tentative.html new file mode 100644 index 0000000000..c4413f7804 --- /dev/null +++ b/testing/web-platform/tests/feature-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 = "/feature-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/feature-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html b/testing/web-platform/tests/feature-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html new file mode 100644 index 0000000000..411e4f2fa0 --- /dev/null +++ b/testing/web-platform/tests/feature-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]}}/feature-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/feature-policy/experimental-features/resources/common.js b/testing/web-platform/tests/feature-policy/experimental-features/resources/common.js new file mode 100644 index 0000000000..4266e39495 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/experimental-features/resources/common.js @@ -0,0 +1,94 @@ +const url_base = "/feature-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/feature-policy/experimental-features/resources/feature-policy-trust-token-redemption.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/feature-policy-trust-token-redemption.html new file mode 100644 index 0000000000..fa5f38b62f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/experimental-features/resources/feature-policy-trust-token-redemption.html @@ -0,0 +1,55 @@ +<script> + 'use strict'; + + window.onload = function() { + // When the trust-token-redemption feature policy is enabled, redemption + // and signing ("send-redemption-record") should both be available; when it's disabled, + // they should both be unavailable. Send the number of available operations + // upstream in order to enforce this in assertions. + let num_enabled = 4; + try { + new Request("https://issuer.example/", { + trustToken: { + type: "token-redemption" + } + }); + } catch (e) { + num_enabled--; + } + try { + new Request("https://destination.example/", { + trustToken: { + type: "send-redemption-record", + issuers: ["https://issuer.example/"] + } + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://issuer.example/"); + xhr.setTrustToken({ + type: "token-redemption" + }); + } catch (e) { + num_enabled--; + } + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://destination.example/"); + xhr.setTrustToken({ + type: "send-redemption-record", + issuers: ["https://issuer.example/"] + }); + } catch (e) { + num_enabled--; + } + + parent.postMessage({ + num_operations_enabled: num_enabled + }, '*'); + } +</script> diff --git a/testing/web-platform/tests/feature-policy/experimental-features/resources/focus-without-user-activation-iframe-tentative.html b/testing/web-platform/tests/feature-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/feature-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/feature-policy/experimental-features/resources/lazyload-contents.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/lazyload-contents.html new file mode 100644 index 0000000000..a6e98c24e6 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/resources/lazyload.png b/testing/web-platform/tests/feature-policy/experimental-features/resources/lazyload.png Binary files differnew file mode 100644 index 0000000000..fd3da53a29 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/experimental-features/resources/lazyload.png diff --git a/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-scrollable-content.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-scrollable-content.html new file mode 100644 index 0000000000..9f78ea4bc2 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html new file mode 100644 index 0000000000..66d0a55104 --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-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/feature-policy/experimental-features/resources/vertical-scroll-scrollintoview.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-scrollintoview.html new file mode 100644 index 0000000000..7bed27c260 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/resources/vertical-scroll-touch-action.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-touch-action.html new file mode 100644 index 0000000000..51b715f30a --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/resources/vertical-scroll-touch-block.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-touch-block.html new file mode 100644 index 0000000000..4c204055af --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/resources/vertical-scroll-wheel-block.html b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll-wheel-block.html new file mode 100644 index 0000000000..21fc2b9b39 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/resources/vertical-scroll.js b/testing/web-platform/tests/feature-policy/experimental-features/resources/vertical-scroll.js new file mode 100644 index 0000000000..88835cc602 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/experimental-features/trust-token-redemption-default-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/feature-policy/experimental-features/trust-token-redemption-default-feature-policy.tentative.https.sub.html new file mode 100644 index 0000000000..134ccc084d --- /dev/null +++ b/testing/web-platform/tests/feature-policy/experimental-features/trust-token-redemption-default-feature-policy.tentative.https.sub.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<title>Test that trust token redemption is enabled/disabled according to the feature policy</title> + +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/feature-policy/resources/featurepolicy.js></script> + <script> + 'use strict'; + const same_origin_src = '/feature-policy/experimental-features/resources/feature-policy-trust-token-redemption.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Default "trust-token-redemption" feature policy ["self"]'; + + test(() => { + try { + // The feature policy gates redemption and signing via both the Fetch + // and XHR interfaces. + new Request("https://issuer.example/", { + trustToken: { + type: "token-redemption" + } + }); + new Request("https://destination.example/", { + trustToken: { + type: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + } + }); + + const redemption_xhr = new XMLHttpRequest(); + redemption_xhr.open("GET", "https://issuer.example/"); + redemption_xhr.setTrustToken({ + type: "token-redemption" + }); + + const signing_xhr = new XMLHttpRequest(); + signing_xhr.open("GET", "https://destination.example/"); + signing_xhr.setTrustToken({ + type: "send-redemption-record", // signing + issuers: ["https://issuer.example/"] + }); + } catch (e) { + assert_unreached(); + } + }, header + ' allows the top-level document.'); + + async_test(t => { + test_feature_availability('Trust token redemption', t, same_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 4, desc); + }); + }, header + ' allows same-origin iframes.'); + + async_test(t => { + test_feature_availability('Trust token redemption', t, cross_origin_src, + (data, desc) => { + assert_equals(data.num_operations_enabled, 0, desc); + }); + }, header + ' disallows cross-origin iframes.'); + </script> +</body> diff --git a/testing/web-platform/tests/feature-policy/experimental-features/trust-token-redemption-supported-by-feature-policy.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/trust-token-redemption-supported-by-feature-policy.tentative.html new file mode 100644 index 0000000000..e349eadc5d --- /dev/null +++ b/testing/web-platform/tests/feature-policy/experimental-features/trust-token-redemption-supported-by-feature-policy.tentative.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<title>Test that trust token redemption is advertised in the feature list</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + test(() => { + assert_in_array('trust-token-redemption', document.featurePolicy.features()); + }, 'document.featurePolicy.features should advertise trust token redemption.'); +</script> diff --git a/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-disabled-frame-no-scroll-manual.tentative.html new file mode 100644 index 0000000000..6f827c919a --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/common.js"></script> +<script src="/feature-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/feature-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-disabled-scrollbar-tentative.html new file mode 100644 index 0000000000..6036234de1 --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/vertical-scroll-scrollbar-ref.html?no-vertical-scrollbar"> +<iframe src="/feature-policy/experimental-features/resources/vertical-scroll-scrollable-content.html" allow="vertical-scroll 'none'"></iframe> diff --git a/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html new file mode 100644 index 0000000000..34cf142d45 --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/common.js"></script> +<script src="/feature-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/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers new file mode 100644 index 0000000000..44072fce56 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-main-frame-manual.tentative.html.headers @@ -0,0 +1 @@ +Feature-Policy: vertical-scroll 'none' diff --git a/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-scrollintoview.tentative.html new file mode 100644 index 0000000000..689685a497 --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/common.js"></script> +<script src="/feature-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/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-touch-action-manual.tentative.html new file mode 100644 index 0000000000..c1b5f1d076 --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/common.js"></script> +<script src="/feature-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/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html new file mode 100644 index 0000000000..82f6ca527f --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/common.js"></script> +<script src="/feature-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/feature-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html b/testing/web-platform/tests/feature-policy/experimental-features/vertical-scroll-wheel-block-manual.tentative.html new file mode 100644 index 0000000000..398aa1f5af --- /dev/null +++ b/testing/web-platform/tests/feature-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="/feature-policy/experimental-features/resources/common.js"></script> +<script src="/feature-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/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html new file mode 100644 index 0000000000..63cd9853dc --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-all.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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-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(Feature-Policy,fullscreen '; + var pipe_end = ';)'; + var header_policies = ["*", "'self'", "'none'"]; + + // 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 cross origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-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/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..111121a52f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen *; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..9cf56ec798 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html @@ -0,0 +1,186 @@ +<!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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-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: false, 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: false, dataOriginTestExpect: false}]; + var pipe_front = '?pipe=sub|header(Feature-Policy,fullscreen '; + var pipe_end = ';)'; + var header_policies = ["*", "'self'", "'none'"]; + + // 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. None of these + // frames should be allowed to use fullscreen, as the header prohibits any + // cross-origin use.` + 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, false, 'fullscreen', false, true); + }, 'Test frame policy on sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, false, '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, false, 'fullscreen', false, true); + }, 'Test frame policy on srcdoc sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, false, '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, false, 'fullscreen', false, true); + }, 'Test frame policy on srcdoc + cross origin sandboxed iframe with allow="fullscreen".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, false, 'fullscreen ' + cross_origin, false, true); + }, 'Test frame policy on sandboxed srcdoc iframe with allow="fullscreen ' + cross_origin + '".'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-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/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.sub.headers new file mode 100644 index 0000000000..0cc259b24f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self'; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some-override.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some-override.https.sub.html new file mode 100644 index 0000000000..744c950248 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-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 feature policy with same_origin_src and cross_origin_src. + var policies = [ + {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: false, 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/feature-policy/feature-policy-frame-policy-allowed-for-some-override.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some-override.https.sub.html.sub.headers new file mode 100644 index 0000000000..c2493a0890 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some-override.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self' https://{{domains[www]}}:{{ports[https][0]}} https://www.example.com; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html new file mode 100644 index 0000000000..5dfd8430d2 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-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 feature policy with same_origin_src and cross_origin_src. + var policies = [ + {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: false, 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(Feature-Policy,fullscreen '; + var pipe_end = ';)'; + var header_policies = ["*", "'self'", "'none'"]; + + // 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', cross_origin_src1, undefined, false); + }, 'Test frame policy on another cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', undefined, true, true); + }, 'Test frame policy on srcdoc iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', same_origin_src, true, true); + }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, true, true); + }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src1, true, true); + }, 'Test frame policy on srcdoc + another cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', data_src, undefined, false); + }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src1, undefined, + policies[i].crossOrigin1TestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src1, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + another cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src1 + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOrigin1TestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".'); + } + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.sub.headers new file mode 100644 index 0000000000..c2493a0890 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self' https://{{domains[www]}}:{{ports[https][0]}} https://www.example.com; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html new file mode 100644 index 0000000000..7ac821d885 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-Policy: fullscreen 'none'; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/feature-policy/resources/feature-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(Feature-Policy,fullscreen '; + var pipe_end = ';)'; + var header_policies = ["*", "'self'", "'none'"]; + + // Test that frame.policy inherits from parent's header policy when allow + // attribute is not specified. + test(function() { + test_frame_policy('fullscreen', same_origin_src, undefined, false); + }, 'Test frame policy on same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, undefined, false); + }, 'Test frame policy on cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', undefined, true, false); + }, 'Test frame policy on srcdoc iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', same_origin_src, true, false); + }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', cross_origin_src, true, false); + }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.'); + test(function() { + test_frame_policy('fullscreen', data_src, undefined, false); + }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.'); + + // Test frame policy with allow attribute set to be one of the policies above. + for (var i = 0; i < policies.length; i++) { + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, undefined, + policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, undefined, + policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', undefined, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".'); + test(function() { + test_frame_policy( + 'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".'); + } + + // Test that the header policy of the iframe document does not change the + // frame policy. + for (var i = 0; i < policies.length; i++) { + for (var j = 0; j < header_policies.length; j++) { + test(function() { + test_frame_policy( + 'fullscreen', + same_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].sameOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".'); + test(function() { + test_frame_policy( + 'fullscreen', + cross_origin_src + pipe_front + header_policies[j] + pipe_end, + undefined, policies[i].crossOriginTestExpect, + 'fullscreen ' + policies[i].allow + ';'); + }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + + '" and header policy = "Feature-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/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..961d40336a --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'none'; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-timing-iframe-camera.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-timing-iframe-camera.https.sub.html new file mode 100644 index 0000000000..995ac2134e --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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/feature-policy/feature-policy-frame-policy-timing.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-timing.https.sub.html new file mode 100644 index 0000000000..c78d16132b --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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 = 'feature-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/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html new file mode 100644 index 0000000000..a036859451 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var header_policy = 'Feature-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/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..111121a52f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen *; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..b6fbdd2cc2 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var header_policy = 'Feature-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/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html.sub.headers new file mode 100644 index 0000000000..0cc259b24f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self'; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html new file mode 100644 index 0000000000..5eda8baa08 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var header_policy = 'Feature-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 disallowed 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 + 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); + 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/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html.sub.headers new file mode 100644 index 0000000000..c2493a0890 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self' https://{{domains[www]}}:{{ports[https][0]}} https://www.example.com; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-header-policy-declined.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-declined.https.sub.html new file mode 100644 index 0000000000..17378e3e13 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-declined.https.sub.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-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 = '/feature-policy/resources/feature-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var cross_origin_src1 = cross_origin1 + same_origin_src; + var header_policy = 'Feature-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 disallowed 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/feature-policy/feature-policy-header-policy-declined.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-declined.https.sub.html.sub.headers new file mode 100644 index 0000000000..4ac2a4a588 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-declined.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen https://{{domains[www]}}:{{ports[https][0]}} https://www.example.com; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html new file mode 100644 index 0000000000..936f261937 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.js></script> + <!-- Feature-Policy: fullscreen 'none'; --> + <script> + 'use strict'; + var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}'; + var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}'; + var same_origin_src = '/feature-policy/resources/feature-policy-allowedfeatures.html'; + var cross_origin_src = cross_origin + same_origin_src; + var header_policy = 'Feature-Policy: fullscreen \'none\''; + + // Test that fullscreen's allowlist is [] + test(function() { + assert_array_equals( + document.featurePolicy.getAllowlistForFeature('fullscreen'), + []); + assert_false(document.fullscreenEnabled, "fullscreenEnabled should reflect feature 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/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..04d160ae10 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.sub.headers @@ -0,0 +1,2 @@ +Feature-Policy: fullscreen 'none'; + diff --git a/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html new file mode 100644 index 0000000000..184cd01342 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.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 = '/feature-policy/resources/feature-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', '\'none\'', 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 \'none\'"'); + + /* ------------------------------------------- + | 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', '\'none\'', 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 \'none\'"'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..111121a52f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen *; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..fe857c916c --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.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 = '/feature-policy/resources/feature-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', '\'none\'', 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 \'none\'"'); + + /* ------------------------------------------- + | 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', '\'none\'', 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 \'none\'"'); + </script> +</body> diff --git a/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html.sub.headers new file mode 100644 index 0000000000..0cc259b24f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self'; diff --git a/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html new file mode 100644 index 0000000000..6194a33b1d --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-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=/feature-policy/resources/featurepolicy.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 = '/feature-policy/resources/feature-policy-nested-subframe-policy.https.sub.html'; + const cross_origin_src = cross_origin + same_origin_src; + const policies = ['*', '\'self\'', '\'none\'']; + + 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/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html.sub.headers b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html.sub.headers new file mode 100644 index 0000000000..961d40336a --- /dev/null +++ b/testing/web-platform/tests/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html.sub.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'none'; diff --git a/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html new file mode 100644 index 0000000000..df53af2236 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script> + 'use strict'; + var relative_path = '/feature-policy/resources/feature-policy-payment.html'; + var base_src = '/feature-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 = 'Feature-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/feature-policy/payment-allowed-by-feature-policy-attribute.https.sub.html b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy-attribute.https.sub.html new file mode 100644 index 0000000000..eeebe399ac --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script> + 'use strict'; + var same_origin_src = '/feature-policy/resources/feature-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var feature_name = 'Feature 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/feature-policy/payment-allowed-by-feature-policy.https.sub.html b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy.https.sub.html new file mode 100644 index 0000000000..b69fa62c05 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy.https.sub.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/feature-policy/resources/featurepolicy.js></script> + <script> + 'use strict'; + var same_origin_src = '/feature-policy/resources/feature-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var header = 'Feature-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/feature-policy/payment-allowed-by-feature-policy.https.sub.html.headers b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy.https.sub.html.headers new file mode 100644 index 0000000000..a5c010bd68 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-allowed-by-feature-policy.https.sub.html.headers @@ -0,0 +1 @@ +Feature-Policy: payment * diff --git a/testing/web-platform/tests/feature-policy/payment-default-feature-policy.https.sub.html b/testing/web-platform/tests/feature-policy/payment-default-feature-policy.https.sub.html new file mode 100644 index 0000000000..f73ac82ab7 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-default-feature-policy.https.sub.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/feature-policy/resources/featurepolicy.js></script> + <script> + 'use strict'; + var same_origin_src = '/feature-policy/resources/feature-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var header = 'Default "payment" feature policy ["self"]'; + + 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/feature-policy/payment-disabled-by-feature-policy.https.sub.html b/testing/web-platform/tests/feature-policy/payment-disabled-by-feature-policy.https.sub.html new file mode 100644 index 0000000000..0e479fd2a3 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-disabled-by-feature-policy.https.sub.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<body> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/feature-policy/resources/featurepolicy.js></script> + <script> + 'use strict'; + var same_origin_src = '/feature-policy/resources/feature-policy-payment.html'; + var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + var header = 'Feature-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/feature-policy/payment-disabled-by-feature-policy.https.sub.html.headers b/testing/web-platform/tests/feature-policy/payment-disabled-by-feature-policy.https.sub.html.headers new file mode 100644 index 0000000000..a2836778bc --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-disabled-by-feature-policy.https.sub.html.headers @@ -0,0 +1 @@ +Feature-Policy: payment 'none' diff --git a/testing/web-platform/tests/feature-policy/payment-supported-by-feature-policy.tentative.html b/testing/web-platform/tests/feature-policy/payment-supported-by-feature-policy.tentative.html new file mode 100644 index 0000000000..07dec70176 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/payment-supported-by-feature-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-feature-policy/#dom-featurepolicy-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.featurePolicy.features()); +}, 'document.featurePolicy.features should advertise payment.'); +</script> diff --git a/testing/web-platform/tests/feature-policy/permissions-policy-feature-policy-coexist.https.html b/testing/web-platform/tests/feature-policy/permissions-policy-feature-policy-coexist.https.html new file mode 100644 index 0000000000..72836b0d1f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/permissions-policy-feature-policy-coexist.https.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src=/resources/testharness.js> </script> +<script src=/resources/testharnessreport.js> </script> +<div> + This is a page with following headers: <br> + <div> + Feature-Policy: geolocation 'none', fullscreen 'none'<br> + Permissions-Policy: geolocation=self, payment=() + </div> +</div> +<script> + const policy = document.featurePolicy; + test(() => { + assert_true(policy.allowsFeature('geolocation')); + }, "When there is conflict in Feature Policy header and Permissions Policy" + + "header, Permissions Policy wins."); + + test(() => { + assert_false(policy.allowsFeature('fullscreen')); + assert_false(policy.allowsFeature('payment')); + }, "When there is no conflict, Feature Policy and Permissions Policy should " + + "all be able to control each feature by themselves."); +</script> + diff --git a/testing/web-platform/tests/feature-policy/permissions-policy-feature-policy-coexist.https.html.headers b/testing/web-platform/tests/feature-policy/permissions-policy-feature-policy-coexist.https.html.headers new file mode 100644 index 0000000000..100e68e9ad --- /dev/null +++ b/testing/web-platform/tests/feature-policy/permissions-policy-feature-policy-coexist.https.html.headers @@ -0,0 +1,2 @@ +Feature-Policy: geolocation 'none', fullscreen 'none' +Permissions-Policy: geolocation=self, payment=() diff --git a/testing/web-platform/tests/feature-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html b/testing/web-platform/tests/feature-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html new file mode 100644 index 0000000000..32df2b71c3 --- /dev/null +++ b/testing/web-platform/tests/feature-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=/feature-policy/resources/featurepolicy.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 = '/feature-policy/resources/feature-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/feature-policy/permissions-policy-header-policy-allowed-for-self.https.sub.html.sub.headers b/testing/web-platform/tests/feature-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/feature-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/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html new file mode 100644 index 0000000000..a5ea5139a9 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script src=/feature-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const relative_path = '/feature-policy/resources/feature-policy-picture-in-picture.html'; + const base_src = '/feature-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 = 'Feature-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/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute.https.sub.html b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute.https.sub.html new file mode 100644 index 0000000000..b0e160e938 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script src=/feature-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const feature_name = 'Feature 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/feature-policy/picture-in-picture-allowed-by-feature-policy.https.sub.html b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-policy.https.sub.html new file mode 100644 index 0000000000..b09335a6de --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script src=/feature-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Feature-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/feature-policy/picture-in-picture-allowed-by-feature-policy.https.sub.html.headers b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-policy.https.sub.html.headers new file mode 100644 index 0000000000..0204b73b18 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-allowed-by-feature-policy.https.sub.html.headers @@ -0,0 +1 @@ +Feature-Policy: picture-in-picture * diff --git a/testing/web-platform/tests/feature-policy/picture-in-picture-default-feature-policy.https.sub.html b/testing/web-platform/tests/feature-policy/picture-in-picture-default-feature-policy.https.sub.html new file mode 100644 index 0000000000..477cf7ba51 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-default-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script src=/feature-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Default "picture-in-picture" feature 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/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html b/testing/web-platform/tests/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html new file mode 100644 index 0000000000..513d04c2fa --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-disabled-by-feature-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=/feature-policy/resources/featurepolicy.js></script> + <script src=/feature-policy/resources/picture-in-picture.js></script> + <script> + 'use strict'; + const same_origin_src = '/feature-policy/resources/feature-policy-picture-in-picture.html'; + const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + + same_origin_src; + const header = 'Feature-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/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.headers b/testing/web-platform/tests/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.headers new file mode 100644 index 0000000000..1759381fdc --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.headers @@ -0,0 +1 @@ +Feature-Policy: picture-in-picture 'none' diff --git a/testing/web-platform/tests/feature-policy/picture-in-picture-supported-by-feature-policy.html b/testing/web-platform/tests/feature-policy/picture-in-picture-supported-by-feature-policy.html new file mode 100644 index 0000000000..a65c682a6a --- /dev/null +++ b/testing/web-platform/tests/feature-policy/picture-in-picture-supported-by-feature-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-feature-policy/#dom-featurepolicy-features"> +<link rel="help" href="https://wicg.github.io/picture-in-picture/#feature-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/feature-policy/policy-extends-to-sandbox.html b/testing/web-platform/tests/feature-policy/policy-extends-to-sandbox.html new file mode 100644 index 0000000000..d45e64f49b --- /dev/null +++ b/testing/web-platform/tests/feature-policy/policy-extends-to-sandbox.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<title>Feature 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 = "/feature-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/feature-policy/reporting/camera-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/camera-reporting.https.html new file mode 100644 index 0000000000..708d3fa525 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/camera-reporting.https.html @@ -0,0 +1,34 @@ +<!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/feature-policy/reporting/camera-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/camera-reporting.https.html.headers new file mode 100644 index 0000000000..2adc5e237f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/camera-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: camera 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/encrypted-media-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/encrypted-media-reporting.https.html new file mode 100644 index 0000000000..32a5a2cc48 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/encrypted-media-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/encrypted-media-reporting.https.html.headers new file mode 100644 index 0000000000..73753a2e41 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/encrypted-media-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: encrypted-media 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/fullscreen-reporting.html b/testing/web-platform/tests/feature-policy/reporting/fullscreen-reporting.html new file mode 100644 index 0000000000..d7b905744d --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/fullscreen-reporting.html.headers b/testing/web-platform/tests/feature-policy/reporting/fullscreen-reporting.html.headers new file mode 100644 index 0000000000..d35e48ba40 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/fullscreen-reporting.html.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/generic-sensor-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/generic-sensor-reporting.https.html new file mode 100644 index 0000000000..c29c069ee3 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/generic-sensor-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/generic-sensor-reporting.https.html.headers new file mode 100644 index 0000000000..80cc027530 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/generic-sensor-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: ambient-light-sensor 'none'; accelerometer 'none'; gyroscope 'none'; magnetometer 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/geolocation-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/geolocation-reporting.https.html new file mode 100644 index 0000000000..e0eb275bcc --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/geolocation-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/geolocation-reporting.https.html.headers new file mode 100644 index 0000000000..7e75481ea6 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/geolocation-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: geolocation 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/image.bmp b/testing/web-platform/tests/feature-policy/reporting/image.bmp Binary files differnew file mode 100644 index 0000000000..f2b88690fc --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/image.bmp diff --git a/testing/web-platform/tests/feature-policy/reporting/microphone-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/microphone-reporting.https.html new file mode 100644 index 0000000000..14ae976cde --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/microphone-reporting.https.html @@ -0,0 +1,34 @@ +<!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/feature-policy/reporting/microphone-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/microphone-reporting.https.html.headers new file mode 100644 index 0000000000..a86e0a0778 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/microphone-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: microphone 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/midi-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/midi-reporting.https.html new file mode 100644 index 0000000000..6cc07f5371 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/midi-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/midi-reporting.https.html.headers new file mode 100644 index 0000000000..0e145978a0 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/midi-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: midi 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/payment-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/payment-reporting.https.html new file mode 100644 index 0000000000..3c04db864d --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-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/feature-policy/reporting/payment-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/payment-reporting.https.html.headers new file mode 100644 index 0000000000..a2836778bc --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/payment-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: payment 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/picture-in-picture-reporting.html b/testing/web-platform/tests/feature-policy/reporting/picture-in-picture-reporting.html new file mode 100644 index 0000000000..177e4d5c02 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/picture-in-picture-reporting.html.headers b/testing/web-platform/tests/feature-policy/reporting/picture-in-picture-reporting.html.headers new file mode 100644 index 0000000000..1759381fdc --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/picture-in-picture-reporting.html.headers @@ -0,0 +1 @@ +Feature-Policy: picture-in-picture 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html new file mode 100644 index 0000000000..c96d8f878a --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/serial-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html.headers new file mode 100644 index 0000000000..be3e6afd42 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/serial-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: serial 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/sync-xhr-reporting.html b/testing/web-platform/tests/feature-policy/reporting/sync-xhr-reporting.html new file mode 100644 index 0000000000..416edf0cbb --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-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/feature-policy/reporting/sync-xhr-reporting.html.headers b/testing/web-platform/tests/feature-policy/reporting/sync-xhr-reporting.html.headers new file mode 100644 index 0000000000..21a909e1fb --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/sync-xhr-reporting.html.headers @@ -0,0 +1 @@ +Feature-Policy: sync-xhr 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/usb-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/usb-reporting.https.html new file mode 100644 index 0000000000..1ec5ba4370 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/usb-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/usb-reporting.https.html.headers new file mode 100644 index 0000000000..4fd1e26936 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/usb-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: usb 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/vr-report-only.https.html b/testing/web-platform/tests/feature-policy/reporting/vr-report-only.https.html new file mode 100644 index 0000000000..b64a2015f7 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/vr-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> +const check_report_format = ([reports, observer]) => { + const report = reports[0]; + assert_equals(report.type, "feature-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "vr"); + 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: ['feature-policy-violation']}).observe(); + }); + await navigator.getVRDisplays(); + check_report_format(await report); +}, "VR report only mode"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/feature-policy/reporting/vr-report-only.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/vr-report-only.https.html.headers new file mode 100644 index 0000000000..0761021f45 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/vr-report-only.https.html.headers @@ -0,0 +1 @@ +Feature-Policy-Report-Only: vr 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/vr-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/vr-reporting.https.html new file mode 100644 index 0000000000..b47f7e187e --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/vr-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, "feature-policy-violation"); + assert_equals(report.url, document.location.href); + assert_equals(report.body.featureId, "vr"); + 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: ['feature-policy-violation']}).observe(); + }); + await promise_rejects_dom(t, 'SecurityError', navigator.getVRDisplays(), + "VR device access should not be allowed in this document."); + const [reports, observer] = await report; + check_report_format(reports, observer); +}, "VR Report Format"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/feature-policy/reporting/vr-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/vr-reporting.https.html.headers new file mode 100644 index 0000000000..d021af7563 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/vr-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: vr 'none' diff --git a/testing/web-platform/tests/feature-policy/reporting/xr-reporting.https.html b/testing/web-platform/tests/feature-policy/reporting/xr-reporting.https.html new file mode 100644 index 0000000000..d87d4fb028 --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/reporting/xr-reporting.https.html.headers b/testing/web-platform/tests/feature-policy/reporting/xr-reporting.https.html.headers new file mode 100644 index 0000000000..2c75896233 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/reporting/xr-reporting.https.html.headers @@ -0,0 +1 @@ +Feature-Policy: xr-spatial-tracking 'none' diff --git a/testing/web-platform/tests/feature-policy/resources/autoplay.js b/testing/web-platform/tests/feature-policy/resources/autoplay.js new file mode 100644 index 0000000000..56780cf6dc --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/resources/feature-policy-allowedfeatures.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-allowedfeatures.html new file mode 100644 index 0000000000..f4b020273f --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-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/feature-policy/resources/feature-policy-autoplay.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-autoplay.html new file mode 100644 index 0000000000..79f8eefb9d --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-autoplay.html @@ -0,0 +1,11 @@ +<script src="/common/media.js"></script> +<script src=/feature-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/feature-policy/resources/feature-policy-clipboard-read.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-clipboard-read.html new file mode 100644 index 0000000000..10fc45fd93 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-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/feature-policy/resources/feature-policy-clipboard-write.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-clipboard-write.html new file mode 100644 index 0000000000..7eb96e3db0 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-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/feature-policy/resources/feature-policy-generic-sensor.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-generic-sensor.html new file mode 100644 index 0000000000..59652e2e7a --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-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/feature-policy/resources/feature-policy-nested-subframe-policy.https.sub.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-nested-subframe-policy.https.sub.html new file mode 100644 index 0000000000..30525d8a3c --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-nested-subframe-policy.https.sub.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<body> +<script> +'use strict'; +const same_origin_src = '/feature-policy/resources/feature-policy-allowedfeatures.html'; +const cross_origin_src = 'https://{{domains[www1]}}:{{ports[https][0]}}' + same_origin_src; +const subframe_header_policy = '?pipe=header(Feature-Policy, fullscreen '; +const policy_all = '*'; +const policy_self = '\'self\''; +const policy_none = '\'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/feature-policy/resources/feature-policy-payment.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-payment.html new file mode 100644 index 0000000000..401a86eb23 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-payment.html @@ -0,0 +1,16 @@ +<script> +'use strict'; + +window.onload = function() { + var supportedInstruments = [ { supportedMethods: [ 'visa' ] } ]; + var details = { + total: { label: 'Test', amount: { currency: 'USD', value: '5.00' } } + }; + try { + new PaymentRequest(supportedInstruments, details); + parent.postMessage({ enabled: true }, '*'); + } catch (e) { + parent.postMessage({ enabled: false }, '*'); + } +} +</script> diff --git a/testing/web-platform/tests/feature-policy/resources/feature-policy-picture-in-picture.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-picture-in-picture.html new file mode 100644 index 0000000000..2f33c44953 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-picture-in-picture.html @@ -0,0 +1,11 @@ +<script src=/common/media.js></script> +<script src=/feature-policy/resources/picture-in-picture.js></script> +<script> +'use strict'; + +window.addEventListener('load', () => { + isPictureInPictureAllowed().then(result => { + window.parent.postMessage({ enabled: result }, '*'); + }); +}, { once: true }); +</script> diff --git a/testing/web-platform/tests/feature-policy/resources/feature-policy-report-json.js b/testing/web-platform/tests/feature-policy/resources/feature-policy-report-json.js new file mode 100644 index 0000000000..08a0ecaded --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-report-json.js @@ -0,0 +1,20 @@ +/** + * @fileoverview functions for ensuring feature 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/feature-policy/resources/feature-policy-webvr.html b/testing/web-platform/tests/feature-policy/resources/feature-policy-webvr.html new file mode 100644 index 0000000000..64a152bf1c --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/feature-policy-webvr.html @@ -0,0 +1,9 @@ +<script> +'use strict'; + +Promise.resolve().then(() => navigator.getVRDisplays()).then(displays => { + window.parent.postMessage({ enabled: true }, '*'); +}, error => { + window.parent.postMessage({ enabled: false }, '*'); +}); +</script> diff --git a/testing/web-platform/tests/feature-policy/resources/featurepolicy.js b/testing/web-platform/tests/feature-policy/resources/featurepolicy.js new file mode 100644 index 0000000000..e9b8804f96 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/featurepolicy.js @@ -0,0 +1,454 @@ +// Feature test to avoid timeouts +function assert_feature_policy_supported() { + assert_not_equals(document.featurePolicy, undefined, + 'Feature Policy is supported'); +} +// Tests whether a feature that is enabled/disabled by feature 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: +// "/feature-policy/resources/feature-policy-payment.html", +// "/feature-policy/resources/feature-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 as a +// postMessage. 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://wicg.github.io/feature-policy/#features). +// See examples at: +// https://github.com/WICG/feature-policy/blob/master/features.md +// allow_attribute: Optional argument, only used for testing fullscreen +// by passing "allowfullscreen". +function test_feature_availability( + feature_description, test, src, expect_feature_available, feature_name, + allow_attribute) { + let frame = document.createElement('iframe'); + frame.src = src; + + if (typeof feature_name !== 'undefined') { + frame.allow = frame.allow.concat(";" + feature_name); + } + + if (typeof allow_attribute !== 'undefined') { + frame.setAttribute(allow_attribute, true); + } + + window.addEventListener('message', test.step_func(function handler(evt) { + if (evt.source === frame.contentWindow) { + expect_feature_available(evt.data, feature_description); + document.body.removeChild(frame); + window.removeEventListener('message', handler); + test.done(); + } + })); + + document.body.appendChild(frame); +} + +// Default helper functions to test a feature's availability: +function expect_feature_available_default(data, feature_description) { + assert_true(data.enabled, feature_description); +} + +function expect_feature_unavailable_default(data, feature_description) { + assert_false(data.enabled, feature_description); +} + +// This is the same as test_feature_availability() but instead of passing in a +// function to check the result of the message sent back from an iframe, instead +// just compares the result to an expected result passed in. +// Arguments: +// test: test created by testharness. Examples: async_test, promise_test. +// src: the URL to load in an iframe in which to test the feature. +// expected_result: the expected value to compare to the data passed back +// from the src page by postMessage. +// allow_attribute: Optional argument, only provided when an allow +// attribute should be specified on the iframe. +function test_feature_availability_with_post_message_result( + test, src, expected_result, allow_attribute) { + var test_result = function(data, feature_description) { + assert_equals(data, expected_result); + }; + test_feature_availability(null, test, src, test_result, allow_attribute); +} + +// If this page is intended to test the named feature (according to the URL), +// tests the feature availability and posts the result back to the parent. +// Otherwise, does nothing. +function test_feature_in_iframe(feature_name, feature_promise_factory) { + if (location.hash.endsWith(`#${feature_name}`)) { + feature_promise_factory().then( + () => window.parent.postMessage('#OK', '*'), + (e) => window.parent.postMessage('#' + e.name, '*')); + } +} + +// Returns true if the URL for this page indicates that it is embedded in an +// iframe. +function page_loaded_in_iframe() { + return location.hash.startsWith('#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) { + // Append #iframe to the URL so we can detect the iframe'd version of the + // page. + return location.pathname + '#iframe#' + 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 feature 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 suceeds when an allow attribute is specified on a +// cross-origin iframe. +// +// 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 + + '" feature policy ["self"] allows the top-level document.'); + + // 2. Allowed in same-origin iframe. + const same_origin_frame_pathname = same_origin_url(feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, '#OK'); + }, + 'Default "' + feature_name + + '" feature policy ["self"] allows same-origin iframes.'); + + // 3. Blocked in cross-origin iframe. + const cross_origin_frame_url = cross_origin_url(cross_origin, feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, '#' + error_name); + }, + 'Default "' + feature_name + + '" feature policy ["self"] disallows cross-origin iframes.'); + + // 4. Allowed in cross-origin iframe with "allow" attribute. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, '#OK', feature_name); + }, + 'Feature policy "' + feature_name + + '" can be enabled in cross-origin iframes using "allow" attribute.'); +} + +// This function runs all feature 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'". +// +// 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 + + '" feature policy ["*"] allows the top-level document.'); + + // 2. Allowed in same-origin iframe. + const same_origin_frame_pathname = same_origin_url(feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, '#OK'); + }, + 'Default "' + feature_name + + '" feature policy ["*"] allows same-origin iframes.'); + + // 3. Allowed in cross-origin iframe. + const cross_origin_frame_url = cross_origin_url(cross_origin, feature_name); + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, '#OK'); + }, + 'Default "' + feature_name + + '" feature policy ["*"] allows cross-origin iframes.'); + + // 4. Blocked in cross-origin iframe with "allow" attribute set to 'none'. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, cross_origin_frame_url, '#' + error_name, + feature_name + " 'none'"); + }, + 'Feature policy "' + feature_name + + '" can be disabled in cross-origin iframes using "allow" attribute.'); + + // 5. Blocked in same-origin iframe with "allow" attribute set to 'none'. + async_test( + t => { + test_feature_availability_with_post_message_result( + t, same_origin_frame_pathname, '#' + error_name, + feature_name + " 'none'"); + }, + 'Feature policy "' + feature_name + + '" can be disabled in same-origin iframes using "allow" attribute.'); +} + +// This function tests that a given policy allows each feature for the correct +// list of origins specified by the |expected_policy|. +// Arguments: +// expected_policy: A list of {feature, allowlist} pairs where the feature is +// enabled for every origin in the allowlist, in the |policy|. +// policy: Either a document.featurePolicy or an iframe.featurePolicy to be +// tested. +// message: A short description of what policy is being tested. +function test_allowlists(expected_policy, policy, message) { + for (var allowlist of allowlists) { + test(function() { + assert_array_equals( + policy.getAllowlistForFeature(allowlist.feature), + allowlist.allowlist); + }, message + ' for feature ' + allowlist.feature); + } +} + +// 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_feature_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_feature_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 'none', defines the frame +// document's header policy on |feature|. +// 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 *, +// 'slef', or 'none'. +// 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_feature_policy_supported() + frame.src = src + '?pipe=sub|header(Feature-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 === '\'none\'') { + 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 === '\'none\'') { + 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.featurePolicy; + 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/feature-policy/resources/nested-sandbox.html b/testing/web-platform/tests/feature-policy/resources/nested-sandbox.html new file mode 100644 index 0000000000..30af207092 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/nested-sandbox.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<title>Return fullscreen feature policy state</title> +<script> + "use strict"; + window.onload = () => { + window.parent.postMessage(document.featurePolicy.allowedFeatures().includes("fullscreen"),"*"); + }; +</script> diff --git a/testing/web-platform/tests/feature-policy/resources/picture-in-picture.js b/testing/web-platform/tests/feature-policy/resources/picture-in-picture.js new file mode 100644 index 0000000000..1bf3c1c12a --- /dev/null +++ b/testing/web-platform/tests/feature-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/feature-policy/resources/redirect-on-load.html b/testing/web-platform/tests/feature-policy/resources/redirect-on-load.html new file mode 100644 index 0000000000..1711655b03 --- /dev/null +++ b/testing/web-platform/tests/feature-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: +// "feature-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/feature-policy/resources/sandbox-self.html b/testing/web-platform/tests/feature-policy/resources/sandbox-self.html new file mode 100644 index 0000000000..3488338f96 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/sandbox-self.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>Return fullscreen feature policy state from self and a sandboxed child frame</title> +<script> + "use strict"; + window.onload = () => { + let frame = document.createElement('iframe'); + frame.src = "/feature-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/feature-policy/resources/sandbox-self.html.headers b/testing/web-platform/tests/feature-policy/resources/sandbox-self.html.headers new file mode 100644 index 0000000000..16c20a7649 --- /dev/null +++ b/testing/web-platform/tests/feature-policy/resources/sandbox-self.html.headers @@ -0,0 +1 @@ +Feature-Policy: fullscreen 'self' |