diff options
Diffstat (limited to 'testing/web-platform/tests/feature-policy/resources')
17 files changed, 737 insertions, 0 deletions
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..864c434c66 --- /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: +// "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' |