summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/permissions-policy/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/permissions-policy/resources
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/permissions-policy/resources')
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/autoplay.js28
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html8
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html15
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html3
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html4
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html8
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html7
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html11
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html9
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html9
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html20
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html20
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html16
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html11
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html22
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html10
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js16
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html10
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html11
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html57
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html60
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html17
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html11
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js20
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html18
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html10
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js14
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html9
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html10
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js14
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html9
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/permissions-policy.js460
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js30
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html11
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/sandbox-self.html23
-rw-r--r--testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers1
36 files changed, 1012 insertions, 0 deletions
diff --git a/testing/web-platform/tests/permissions-policy/resources/autoplay.js b/testing/web-platform/tests/permissions-policy/resources/autoplay.js
new file mode 100644
index 0000000000..56780cf6dc
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/autoplay.js
@@ -0,0 +1,28 @@
+
+
+function simulateGesture(t, callback) {
+ // Get or create the target element.
+ let target = document.getElementById('target');
+ if (!target) {
+ target = document.createElement('button');
+ target.setAttribute('id', 'target');
+ document.body.appendChild(target);
+ }
+
+ // Simulate a gesture in the top frame to remove any gesture based autoplay
+ // restrictions.
+ test_driver.click(target).then(callback, t.unreached_func('click failed'));
+}
+
+function isAutoplayAllowed() {
+ return new Promise((resolve, reject) => {
+ const video = document.createElement('video');
+ video.src = getVideoURI('/media/A4');
+ video.play().then(() => resolve(true), (e) => {
+ if (e.name == 'NotAllowedError')
+ resolve(false);
+ else
+ resolve(true);
+ });
+ });
+}
diff --git a/testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html b/testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html
new file mode 100644
index 0000000000..4ba512140d
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/nested-sandbox.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>Return fullscreen permissions policy state</title>
+<script>
+ "use strict";
+ window.onload = () => {
+ window.parent.postMessage(document.featurePolicy.allowedFeatures().includes("fullscreen"),"*");
+ };
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html
new file mode 100644
index 0000000000..cb1f214f53
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history1.sub.https.html
@@ -0,0 +1,15 @@
+<script>
+ window.addEventListener('message', e => {
+ if (e.data == 'redirect') {
+ location.assign(
+ "https://{{domains[]}}:{{location[port]}}/permissions-policy/resources/opaque-origin-history2.https.html");
+ }
+ });
+
+ parent.postMessage(
+ document.fullscreenEnabled ?
+ 'fullscreen enabled in opaque-origin-history1.html' :
+ 'fullscreen disabled in opaque-origin-history1.html',
+ '*'
+ );
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html
new file mode 100644
index 0000000000..20e63cf48b
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin-history2.https.html
@@ -0,0 +1,3 @@
+<script>
+ history.back();
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html
new file mode 100644
index 0000000000..f8a8c9d1ad
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin1.sub.https.html
@@ -0,0 +1,4 @@
+<script>
+ location.assign(
+ "https://{{domains[]}}:{{location[port]}}/permissions-policy/resources/opaque-origin2.https.html");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html b/testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html
new file mode 100644
index 0000000000..73122ff7f6
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/opaque-origin2.https.html
@@ -0,0 +1,8 @@
+<script>
+ parent.postMessage(
+ document.fullscreenEnabled ?
+ 'fullscreen enabled in opaque-origin2.html' :
+ 'fullscreen disabled in opaque-origin2.html',
+ '*'
+ );
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html
new file mode 100644
index 0000000000..f4b020273f
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-allowedfeatures.html
@@ -0,0 +1,7 @@
+<script>
+'use strict';
+
+window.onload = function() {
+ parent.postMessage(document.featurePolicy.allowedFeatures(), '*');
+}
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html
new file mode 100644
index 0000000000..665d19bea2
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-autoplay.html
@@ -0,0 +1,11 @@
+<script src="/common/media.js"></script>
+<script src=/permissions-policy/resources/autoplay.js></script>
+<script>
+'use strict';
+
+window.addEventListener('load', () => {
+ isAutoplayAllowed().then((result) => {
+ window.parent.postMessage({ enabled: result }, '*');
+ });
+}, { once: true });
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html
new file mode 100644
index 0000000000..643d6a4ef5
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-battery.html
@@ -0,0 +1,9 @@
+<script>
+'use strict';
+
+Promise.resolve().then(() => navigator.getBattery()).then(battery => {
+ window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
+}, error => {
+ window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html
new file mode 100644
index 0000000000..2265bd01d2
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-bluetooth.html
@@ -0,0 +1,9 @@
+<script>
+ 'use strict';
+
+ navigator.bluetooth.getDevices().then(devices => {
+ window.parent.postMessage({ type: 'availability-result', enabled: true}, '*');
+ }, error => {
+ window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+ });
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html
new file mode 100644
index 0000000000..10fc45fd93
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-read.html
@@ -0,0 +1,20 @@
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+'use strict';
+
+(async () => {
+ try {
+ // TODO(https://crbug.com/1074482): Cross-origin focus is asynchronous and
+ // requires user gesture. Implement testing support for cross-origin focus.
+ window.focus(); // The Clipboard API requires focus.
+
+ await test_driver.set_permission({ name: 'clipboard-read' }, 'granted');
+ await navigator.clipboard.readText('test text');
+
+ window.parent.postMessage({ enabled: true }, "*");
+ } catch (e) {
+ window.parent.postMessage({ enabled: false }, "*");
+ }
+})();
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html
new file mode 100644
index 0000000000..7eb96e3db0
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-clipboard-write.html
@@ -0,0 +1,20 @@
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+'use strict';
+
+(async () => {
+ try {
+ // TODO(https://crbug.com/1074482): Cross-origin focus is asynchronous and
+ // requires user gesture. Implement testing support for cross-origin focus.
+ window.focus(); // The Clipboard API requires focus.
+
+ await test_driver.set_permission({ name: 'clipboard-write' }, 'granted');
+ await navigator.clipboard.writeText('test text');
+
+ window.parent.postMessage({ enabled: true }, "*");
+ } catch (e) {
+ window.parent.postMessage({ enabled: false }, "*");
+ }
+})();
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html
new file mode 100644
index 0000000000..fafe71ee04
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-compute-pressure.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script>
+'use strict';
+
+window.onload = async function() {
+ let enabled = true;
+ try {
+ const observer = new PressureObserver(() => {});
+ await observer.observe("cpu");
+ observer.disconnect();
+ } catch (e) {
+ enabled = false;
+ }
+ parent.postMessage({ type: 'availability-result', enabled }, '*');
+}
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html
new file mode 100644
index 0000000000..59652e2e7a
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-generic-sensor.html
@@ -0,0 +1,11 @@
+<script>
+"use strict";
+
+try {
+ const sensorName = location.hash.substring(1);
+ const sensor = new window[sensorName]();
+ window.parent.postMessage({ enabled: true }, "*");
+} catch (e) {
+ window.parent.postMessage({ enabled: false }, "*");
+}
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html
new file mode 100644
index 0000000000..b858a52392
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-geolocation.html
@@ -0,0 +1,22 @@
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+ "use strict";
+
+ Promise.resolve().then(async () => {
+ test_driver.set_test_context(window.parent);
+ await test_driver.set_permission(
+ { name: "geolocation" },
+ "granted"
+ );
+ let enabled = true;
+ try {
+ await new Promise((resolve, reject) => {
+ navigator.geolocation.getCurrentPosition(resolve, reject);
+ });
+ } catch (e) {
+ enabled = false;
+ }
+ window.parent.postMessage({ type: "availability-result", enabled }, "*");
+ });
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html
new file mode 100644
index 0000000000..5bcc398039
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.html
@@ -0,0 +1,10 @@
+<script>
+'use strict';
+
+let worker = new Worker('permissions-policy-idle-detection-worker.js');
+
+worker.onmessage = event => {
+ window.parent.postMessage(event.data, '*');
+};
+worker.postMessage({ type: 'ready' });
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js
new file mode 100644
index 0000000000..0d348c72ae
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection-worker.js
@@ -0,0 +1,16 @@
+'use strict';
+
+// Dedicated worker
+if (typeof postMessage === 'function') {
+ onmessage = event => {
+ switch(event.data.type) {
+ case 'ready':
+ new IdleDetector().start().then(() => {
+ postMessage({ type: 'availability-result', enabled: true });
+ }, error => {
+ postMessage ({ type: 'availability-result', enabled: false });
+ });
+ break;
+ }
+ };
+}
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html
new file mode 100644
index 0000000000..f21a3851d1
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-idle-detection.html
@@ -0,0 +1,10 @@
+<script>
+'use strict';
+
+new IdleDetector().start().then(() => {
+ window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
+}, error => {
+ window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+});
+
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html
new file mode 100644
index 0000000000..1b965d9c7d
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-local-fonts.html
@@ -0,0 +1,11 @@
+<script>
+'use strict';
+
+window.onload = function() {
+ self.queryLocalFonts().then(() => {
+ parent.postMessage({ type: 'availability-result', enabled: true }, '*');
+ }, error => {
+ parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+ });
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html
new file mode 100644
index 0000000000..4c012bbae6
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-nested-subframe-policy.https.sub.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<body>
+<script>
+'use strict';
+const same_origin_src = '/permissions-policy/resources/permissions-policy-allowedfeatures.html';
+const cross_origin_src = 'https://{{domains[www1]}}:{{ports[https][0]}}' + same_origin_src;
+const subframe_header_policy = '?pipe=header(Permissions-Policy,fullscreen=';
+const policy_all = '*';
+const policy_self = 'self';
+const policy_none = '\\(\\)';
+
+// Messages gathered from subframes. When all subframe messages are gathered,
+// it will be send back to top level frame.
+const subframe_messages = [];
+
+let local_frame_all = document.createElement('iframe');
+let local_frame_self = document.createElement('iframe');
+let local_frame_none = document.createElement('iframe');
+local_frame_all.src = same_origin_src + subframe_header_policy + policy_all + ')';
+local_frame_self.src = same_origin_src + subframe_header_policy + policy_self + ')';
+local_frame_none.src = same_origin_src + subframe_header_policy + policy_none + ')';
+
+let remote_frame_all = document.createElement('iframe');
+let remote_frame_self = document.createElement('iframe');
+let remote_frame_none = document.createElement('iframe');
+remote_frame_all.src = cross_origin_src + subframe_header_policy + policy_all + ')';
+remote_frame_self.src = cross_origin_src + subframe_header_policy + policy_self + ')';
+remote_frame_none.src = cross_origin_src + subframe_header_policy + policy_none + ')';
+
+window.addEventListener('message', function(evt) {
+ if (evt.source === local_frame_all.contentWindow) {
+ subframe_messages.push({frame: 'local', policy: policy_all, allowedfeatures: evt.data});
+ } else if (evt.source === local_frame_self.contentWindow) {
+ subframe_messages.push({frame: 'local', policy: policy_self, allowedfeatures: evt.data});
+ } else if (evt.source === local_frame_none.contentWindow) {
+ subframe_messages.push({frame: 'local', policy: policy_none, allowedfeatures: evt.data});
+ } else if (evt.source === remote_frame_all.contentWindow) {
+ subframe_messages.push({frame: 'remote', policy: policy_all, allowedfeatures: evt.data});
+ } else if (evt.source === remote_frame_self.contentWindow) {
+ subframe_messages.push({frame: 'remote', policy: policy_self, allowedfeatures: evt.data});
+ } else if (evt.source === remote_frame_none.contentWindow) {
+ subframe_messages.push({frame: 'remote', policy: policy_none, allowedfeatures: evt.data});
+ }
+
+ if (subframe_messages.length == 6)
+ parent.postMessage(subframe_messages, '*');
+});
+
+document.body.appendChild(local_frame_all);
+document.body.appendChild(local_frame_self);
+document.body.appendChild(local_frame_none);
+document.body.appendChild(remote_frame_all);
+document.body.appendChild(remote_frame_self);
+document.body.appendChild(remote_frame_none);
+</script>
+</body>
+
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html
new file mode 100644
index 0000000000..86f0ea3e48
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html
@@ -0,0 +1,60 @@
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/webauthn/helpers.js"></script>
+<script>
+'use strict';
+
+const textEncoder = new TextEncoder();
+let authenticatorArgs = {
+ protocol: 'ctap2_1',
+ transport: 'internal',
+ hasResidentKey: true,
+ hasUserVerification: true,
+ isUserVerified: true,
+};
+
+window.onload = async function() {
+ await window.test_driver.add_virtual_authenticator(authenticatorArgs);
+ let enabled = true;
+ let message = `OK`;
+ try {
+ const publicKey = {
+ rp: {
+ id: window.location.hostname,
+ name: 'Joe',
+ },
+ user: {
+ name: 'user@domain',
+ id: Uint8Array.from('id', c => c.charCodeAt(0)),
+ displayName: 'User',
+ },
+ challenge: textEncoder.encode('Enrollment challenge'),
+ pubKeyCredParams: [{
+ type: 'public-key',
+ alg: -7, // ECDSA, not supported on Windows.
+ }, {
+ type: 'public-key',
+ alg: -257, // RSA, supported on Windows.
+ }],
+ authenticatorSelection: {
+ userVerification: 'required',
+ residentKey: 'required',
+ authenticatorAttachment: 'platform',
+ },
+ extensions: {
+ payment: {
+ isPayment: true,
+ },
+ }
+ };
+ await window.test_driver.bless('user activation');
+ await navigator.credentials.create({
+ publicKey
+ });
+ } catch (e) {
+ enabled = false;
+ message = e.name + '#' + e.message;
+ }
+ parent.postMessage({ type: 'availability-result', enabled, message }, '*');
+}
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html
new file mode 100644
index 0000000000..641a5e65b6
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment.html
@@ -0,0 +1,17 @@
+<script>
+'use strict';
+
+window.onload = function() {
+ var supportedInstruments = [ { supportedMethods: [ 'visa' ] } ];
+ var details = {
+ total: { label: 'Test', amount: { currency: 'USD', value: '5.00' } }
+ };
+ let enabled = true;
+ try {
+ new PaymentRequest(supportedInstruments, details);
+ } catch (e) {
+ enabled = false;
+ }
+ parent.postMessage({ type: 'availability-result', enabled }, '*');
+}
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html
new file mode 100644
index 0000000000..e512f17ff0
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-picture-in-picture.html
@@ -0,0 +1,11 @@
+<script src=/common/media.js></script>
+<script src=/permissions-policy/resources/picture-in-picture.js></script>
+<script>
+'use strict';
+
+window.addEventListener('load', () => {
+ isPictureInPictureAllowed().then(enabled => {
+ window.parent.postMessage({ type: 'availability-result', enabled }, '*');
+ });
+}, { once: true });
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js
new file mode 100644
index 0000000000..ad84ff9ce4
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-report-json.js
@@ -0,0 +1,20 @@
+/**
+ * @fileoverview functions for ensuring permissions policy report is serializable
+ */
+
+const check_report_json = (report) => {
+ // Ensures toJSON method exists on report.
+ assert_equals(typeof report.toJSON, "function");
+ const report_json = report.toJSON();
+ // Ensures toJSON() call is successful.
+ assert_equals(report.type, report_json.type);
+ assert_equals(report.url, report_json.url);
+ assert_equals(report.body.featureId, report_json.body.featureId);
+ assert_equals(report.body.disposition, report_json.body.disposition);
+ assert_equals(report.body.sourceFile, report_json.body.sourceFile);
+ assert_equals(report.body.lineNumber, report_json.body.lineNumber);
+ assert_equals(report.body.columnNumber, report_json.body.columnNumber);
+ // Ensures JSON.stringify() serializes the report correctly.
+ assert_false(JSON.stringify(report) === "{}");
+ assert_equals(JSON.stringify(report), JSON.stringify(report_json));
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html
new file mode 100644
index 0000000000..b186632251
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-screen-wakelock.html
@@ -0,0 +1,18 @@
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+"use strict";
+
+Promise.resolve().then(async () => {
+ try {
+ await test_driver.set_permission(
+ { name: 'screen-wake-lock' }, 'granted');
+
+ const wakeLock = await navigator.wakeLock.request("screen");
+ await wakeLock.release();
+ window.parent.postMessage({ type: 'availability-result', enabled: true }, "*");
+ } catch (e) {
+ window.parent.postMessage({ type: 'availability-result', enabled: false }, "*");
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html
new file mode 100644
index 0000000000..56bcfaede9
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.html
@@ -0,0 +1,10 @@
+<script>
+'use strict';
+
+let worker = new Worker('permissions-policy-serial-worker.js');
+
+worker.onmessage = event => {
+ window.parent.postMessage(event.data, '*');
+};
+worker.postMessage({ type: 'ready' });
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js
new file mode 100644
index 0000000000..59bb6787dd
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial-worker.js
@@ -0,0 +1,14 @@
+'use strict';
+
+// Dedicated worker
+if (typeof postMessage === 'function') {
+ onmessage = event => {
+ switch(event.data.type) {
+ case 'ready':
+ navigator.serial.getPorts().then(
+ () => postMessage({ type: 'availability-result', enabled: true }),
+ error => postMessage ({ type: 'availability-result', enabled: false }));
+ break;
+ }
+ };
+}
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html
new file mode 100644
index 0000000000..fe25f03482
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-serial.html
@@ -0,0 +1,9 @@
+<script>
+'use strict';
+
+navigator.serial.getPorts().then(ports => {
+ window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
+}, error => {
+ window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html
new file mode 100644
index 0000000000..e50ac55a27
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.html
@@ -0,0 +1,10 @@
+<script>
+'use strict';
+
+let worker = new Worker('permissions-policy-usb-worker.js');
+
+worker.onmessage = event => {
+ window.parent.postMessage(event.data, '*');
+};
+worker.postMessage({ type: 'ready' });
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js
new file mode 100644
index 0000000000..97d96e7fb7
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb-worker.js
@@ -0,0 +1,14 @@
+'use strict';
+
+// Dedicated worker
+if (typeof postMessage === 'function') {
+ onmessage = event => {
+ switch(event.data.type) {
+ case 'ready':
+ navigator.usb.getDevices().then(
+ () => postMessage({ type: 'availability-result', enabled: true }),
+ error => postMessage ({ type: 'availability-result', enabled: false }));
+ break;
+ }
+ };
+}
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html
new file mode 100644
index 0000000000..8812ca7db9
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-usb.html
@@ -0,0 +1,9 @@
+<script>
+'use strict';
+
+Promise.resolve().then(() => navigator.usb.getDevices()).then(devices => {
+ window.parent.postMessage({ type: 'availability-result', enabled: true }, '*');
+}, error => {
+ window.parent.postMessage({ type: 'availability-result', enabled: false }, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js b/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js
new file mode 100644
index 0000000000..00c0bf2326
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy.js
@@ -0,0 +1,460 @@
+// Feature test to avoid timeouts
+function assert_permissions_policy_supported() {
+ assert_not_equals(document.featurePolicy, undefined,
+ 'permissions policy is supported');
+}
+// Tests whether a feature that is enabled/disabled by permissions policy works
+// as expected.
+// Arguments:
+// feature_description: a short string describing what feature is being
+// tested. Examples: "usb.GetDevices()", "PaymentRequest()".
+// test: test created by testharness. Examples: async_test, promise_test.
+// src: URL where a feature's availability is checked. Examples:
+// "/permissions-policy/resources/permissions-policy-payment.html",
+// "/permissions-policy/resources/permissions-policy-usb.html".
+// expect_feature_available: a callback(data, feature_description) to
+// verify if a feature is available or unavailable as expected.
+// The file under the path "src" defines what "data" is sent back via
+// postMessage with type: 'availability-result'.
+// Inside the callback, some tests (e.g., EXPECT_EQ, EXPECT_TRUE, etc)
+// are run accordingly to test a feature's availability.
+// Example: expect_feature_available_default(data, feature_description).
+// feature_name: Optional argument, only provided when testing iframe allow
+// attribute. "feature_name" is the feature name of a policy controlled
+// feature (https://w3c.github.io/webappsec-permissions-policy/#features).
+// See examples at:
+// https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md
+// allow_attribute: Optional argument, only used for testing fullscreen or
+// payment: either "allowfullscreen" or "allowpaymentrequest" is passed.
+function test_feature_availability(
+ feature_description, test, src, expect_feature_available, feature_name,
+ allow_attribute) {
+ let frame = document.createElement('iframe');
+ frame.src = src;
+
+ if (typeof feature_name !== 'undefined') {
+ frame.allow = frame.allow.concat(";" + feature_name);
+ }
+
+ if (typeof allow_attribute !== 'undefined') {
+ frame.setAttribute(allow_attribute, true);
+ }
+
+ window.addEventListener('message', test.step_func(evt => {
+ if (evt.source === frame.contentWindow &&
+ evt.data.type === 'availability-result') {
+ expect_feature_available(evt.data, feature_description);
+ document.body.removeChild(frame);
+ test.done();
+ }
+ }));
+
+ document.body.appendChild(frame);
+}
+
+// Default helper functions to test a feature's availability:
+function expect_feature_available_default(data, feature_description) {
+ assert_true(data.enabled, feature_description);
+}
+
+function expect_feature_unavailable_default(data, feature_description) {
+ assert_false(data.enabled, feature_description);
+}
+
+// This is the same as test_feature_availability() but instead of passing in a
+// function to check the result of the message sent back from an iframe, instead
+// just compares the result to an expected result passed in.
+// Arguments:
+// test: test created by testharness. Examples: async_test, promise_test.
+// src: the URL to load in an iframe in which to test the feature.
+// expected_result: the expected value to compare to the data passed back
+// from the src page by postMessage.
+// allow_attribute: Optional argument, only provided when an allow
+// attribute should be specified on the iframe.
+function test_feature_availability_with_post_message_result(
+ test, src, expected_result, allow_attribute) {
+ const test_result = ({ name, message }, feature_description) => {
+ assert_equals(name, expected_result, message + '.');
+ };
+ test_feature_availability(null, test, src, test_result, allow_attribute);
+}
+
+// If this page is intended to test the named feature (according to the URL),
+// tests the feature availability and posts the result back to the parent.
+// Otherwise, does nothing.
+async function test_feature_in_iframe(feature_name, feature_promise_factory) {
+ if (location.hash.endsWith(`#${feature_name}`)) {
+ let message = 'Available';
+ let name = '#OK';
+ try {
+ await feature_promise_factory();
+ } catch (e) {
+ ({ name, message } = e);
+ }
+ window.parent.postMessage(
+ { type: 'availability-result', name, message }, '*');
+ }
+}
+
+// Returns true if the URL for this page indicates that it is embedded in an
+// iframe.
+function page_loaded_in_iframe() {
+ return new URLSearchParams(location.search).get('in-iframe');
+}
+
+// Returns a same-origin (relative) URL suitable for embedding in an iframe for
+// testing the availability of the feature.
+function same_origin_url(feature_name) {
+ // Add an "in-iframe" query parameter so that we can detect the iframe'd
+ // version of the page and testharness script loading can be disabled in
+ // that version, as required for use of testdriver in non-toplevel browsing
+ // contexts.
+ return location.pathname + '?in-iframe=yes#' + feature_name;
+}
+
+// Returns a cross-origin (absolute) URL suitable for embedding in an iframe for
+// testing the availability of the feature.
+function cross_origin_url(base_url, feature_name) {
+ return base_url + same_origin_url(feature_name);
+}
+
+// This function runs all permissions policy tests for a particular feature that
+// has a default policy of "self". This includes testing:
+// 1. Feature usage succeeds by default in the top level frame.
+// 2. Feature usage succeeds by default in a same-origin iframe.
+// 3. Feature usage fails by default in a cross-origin iframe.
+// 4. Feature usage succeeds when an allow attribute is specified on a
+// cross-origin iframe.
+// 5. Feature usage fails when an allow attribute is specified on a
+// same-origin iframe with a value of "feature-name 'none'".
+//
+// The same page which called this function will be loaded in the iframe in
+// order to test feature usage there. When this function is called in that
+// context it will simply run the feature and return a result back via
+// postMessage.
+//
+// Arguments:
+// cross_origin: A cross-origin URL base to be used to load the page which
+// called into this function.
+// feature_name: The name of the feature as it should be specified in an
+// allow attribute.
+// error_name: If feature usage does not succeed, this is the string
+// representation of the error that will be passed in the rejected
+// promise.
+// feature_promise_factory: A function which returns a promise which tests
+// feature usage. If usage succeeds, the promise should resolve. If it
+// fails, the promise should reject with an error that can be
+// represented as a string.
+function run_all_fp_tests_allow_self(
+ cross_origin, feature_name, error_name, feature_promise_factory) {
+ // This may be the version of the page loaded up in an iframe. If so, just
+ // post the result of running the feature promise back to the parent.
+ if (page_loaded_in_iframe()) {
+ test_feature_in_iframe(feature_name, feature_promise_factory);
+ return;
+ }
+
+ // Run the various tests.
+ // 1. Allowed in top-level frame.
+ promise_test(
+ () => feature_promise_factory(),
+ 'Default "' + feature_name +
+ '" permissions policy ["self"] allows the top-level document.');
+
+ // 2. Allowed in same-origin iframe.
+ const same_origin_frame_pathname = same_origin_url(feature_name);
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, same_origin_frame_pathname, '#OK');
+ },
+ 'Default "' + feature_name +
+ '" permissions policy ["self"] allows same-origin iframes.');
+
+ // 3. Blocked in cross-origin iframe.
+ const cross_origin_frame_url = cross_origin_url(cross_origin, feature_name);
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, cross_origin_frame_url, error_name);
+ },
+ 'Default "' + feature_name +
+ '" permissions policy ["self"] disallows cross-origin iframes.');
+
+ // 4. Allowed in cross-origin iframe with "allow" attribute.
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, cross_origin_frame_url, '#OK', feature_name);
+ },
+ 'permissions policy "' + feature_name +
+ '" can be enabled in cross-origin iframes using "allow" attribute.');
+
+ // 5. Blocked in same-origin iframe with "allow" attribute set to 'none'.
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, same_origin_frame_pathname, error_name,
+ feature_name + " 'none'");
+ },
+ 'permissions policy "' + feature_name +
+ '" can be disabled in same-origin iframes using "allow" attribute.');
+}
+
+// This function runs all permissions policy tests for a particular feature that
+// has a default policy of "*". This includes testing:
+// 1. Feature usage succeeds by default in the top level frame.
+// 2. Feature usage succeeds by default in a same-origin iframe.
+// 3. Feature usage succeeds by default in a cross-origin iframe.
+// 4. Feature usage fails when an allow attribute is specified on a
+// cross-origin iframe with a value of "feature-name 'none'".
+// 5. Feature usage fails when an allow attribute is specified on a
+// same-origin iframe with a value of "feature-name 'none'".
+//
+// The same page which called this function will be loaded in the iframe in
+// order to test feature usage there. When this function is called in that
+// context it will simply run the feature and return a result back via
+// postMessage.
+//
+// Arguments:
+// cross_origin: A cross-origin URL base to be used to load the page which
+// called into this function.
+// feature_name: The name of the feature as it should be specified in an
+// allow attribute.
+// error_name: If feature usage does not succeed, this is the string
+// representation of the error that will be passed in the rejected
+// promise.
+// feature_promise_factory: A function which returns a promise which tests
+// feature usage. If usage succeeds, the promise should resolve. If it
+// fails, the promise should reject with an error that can be
+// represented as a string.
+function run_all_fp_tests_allow_all(
+ cross_origin, feature_name, error_name, feature_promise_factory) {
+ // This may be the version of the page loaded up in an iframe. If so, just
+ // post the result of running the feature promise back to the parent.
+ if (page_loaded_in_iframe()) {
+ test_feature_in_iframe(feature_name, feature_promise_factory);
+ return;
+ }
+
+ // Run the various tests.
+ // 1. Allowed in top-level frame.
+ promise_test(
+ () => feature_promise_factory(),
+ 'Default "' + feature_name +
+ '" permissions policy ["*"] allows the top-level document.');
+
+ // 2. Allowed in same-origin iframe.
+ const same_origin_frame_pathname = same_origin_url(feature_name);
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, same_origin_frame_pathname, '#OK');
+ },
+ 'Default "' + feature_name +
+ '" permissions policy ["*"] allows same-origin iframes.');
+
+ // 3. Allowed in cross-origin iframe.
+ const cross_origin_frame_url = cross_origin_url(cross_origin, feature_name);
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, cross_origin_frame_url, '#OK');
+ },
+ 'Default "' + feature_name +
+ '" permissions policy ["*"] allows cross-origin iframes.');
+
+ // 4. Blocked in cross-origin iframe with "allow" attribute set to 'none'.
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, cross_origin_frame_url, error_name,
+ feature_name + " 'none'");
+ },
+ 'permissions policy "' + feature_name +
+ '" can be disabled in cross-origin iframes using "allow" attribute.');
+
+ // 5. Blocked in same-origin iframe with "allow" attribute set to 'none'.
+ async_test(
+ t => {
+ test_feature_availability_with_post_message_result(
+ t, same_origin_frame_pathname, error_name,
+ feature_name + " 'none'");
+ },
+ 'permissions policy "' + feature_name +
+ '" can be disabled in same-origin iframes using "allow" attribute.');
+}
+
+// This function tests that a subframe's document policy allows a given feature.
+// A feature is allowed in a frame either through inherited policy or specified
+// by iframe allow attribute.
+// Arguments:
+// test: test created by testharness. Examples: async_test, promise_test.
+// feature: feature name that should be allowed in the frame.
+// src: the URL to load in the frame.
+// allow: the allow attribute (container policy) of the iframe
+function test_allowed_feature_for_subframe(message, feature, src, allow) {
+ let frame = document.createElement('iframe');
+ if (typeof allow !== 'undefined') {
+ frame.allow = allow;
+ }
+ promise_test(function() {
+ assert_permissions_policy_supported();
+ frame.src = src;
+ return new Promise(function(resolve, reject) {
+ window.addEventListener('message', function handler(evt) {
+ resolve(evt.data);
+ }, { once: true });
+ document.body.appendChild(frame);
+ }).then(function(data) {
+ assert_true(data.includes(feature), feature);
+ });
+ }, message);
+}
+
+// This function tests that a subframe's document policy disallows a given
+// feature. A feature is allowed in a frame either through inherited policy or
+// specified by iframe allow attribute.
+// Arguments:
+// test: test created by testharness. Examples: async_test, promise_test.
+// feature: feature name that should not be allowed in the frame.
+// src: the URL to load in the frame.
+// allow: the allow attribute (container policy) of the iframe
+function test_disallowed_feature_for_subframe(message, feature, src, allow) {
+ let frame = document.createElement('iframe');
+ if (typeof allow !== 'undefined') {
+ frame.allow = allow;
+ }
+ promise_test(function() {
+ assert_permissions_policy_supported();
+ frame.src = src;
+ return new Promise(function(resolve, reject) {
+ window.addEventListener('message', function handler(evt) {
+ resolve(evt.data);
+ }, { once: true });
+ document.body.appendChild(frame);
+ }).then(function(data) {
+ assert_false(data.includes(feature), feature);
+ });
+ }, message);
+}
+
+// This function tests that a subframe with header policy defined on a given
+// feature allows and disallows the feature as expected.
+// Arguments:
+// feature: feature name.
+// frame_header_policy: either *, self or \\(\\), defines the frame
+// document's header policy on |feature|.
+// '(' and ')' need to be escaped because of server end
+// header parameter syntax limitation.
+// src: the URL to load in the frame.
+// test_expects: contains 6 expected results of either |feature| is allowed
+// or not inside of a local or remote iframe nested inside
+// the subframe given the header policy to be either *,
+// self, or ().
+// test_name: name of the test.
+function test_subframe_header_policy(
+ feature, frame_header_policy, src, test_expects, test_name) {
+ let frame = document.createElement('iframe');
+ promise_test(function() {
+ assert_permissions_policy_supported()
+ frame.src = src + '?pipe=sub|header(Permissions-Policy,' + feature + '='
+ + frame_header_policy + ')';
+ return new Promise(function(resolve) {
+ window.addEventListener('message', function handler(evt) {
+ resolve(evt.data);
+ });
+ document.body.appendChild(frame);
+ }).then(function(results) {
+ for (var j = 0; j < results.length; j++) {
+ var data = results[j];
+
+ function test_result(message, test_expect) {
+ if (test_expect) {
+ assert_true(data.allowedfeatures.includes(feature), message);
+ } else {
+ assert_false(data.allowedfeatures.includes(feature), message);
+ }
+ }
+
+ if (data.frame === 'local') {
+ if (data.policy === '*') {
+ test_result('local_all:', test_expects.local_all);
+ }
+ if (data.policy === 'self') {
+ test_result('local_self:', test_expects.local_self);
+ }
+ if (data.policy === '\\(\\)') {
+ test_result('local_none:', test_expects.local_none);
+ }
+ }
+
+ if (data.frame === 'remote') {
+ if (data.policy === '*') {
+ test_result('remote_all:', test_expects.remote_all);
+ }
+ if (data.policy === 'self') {
+ test_result('remote_self:', test_expects.remote_self);
+ }
+ if (data.policy === '\\(\\)') {
+ test_result('remote_none:', test_expects.remote_none);
+ }
+ }
+ }
+ });
+ }, test_name);
+}
+
+// This function tests that frame policy allows a given feature correctly. A
+// feature is allowed in a frame either through inherited policy or specified
+// by iframe allow attribute.
+// Arguments:
+// feature: feature name.
+// src: the URL to load in the frame. If undefined, the iframe will have a
+// srcdoc="" attribute
+// test_expect: boolean value of whether the feature should be allowed.
+// allow: optional, the allow attribute (container policy) of the iframe.
+// allowfullscreen: optional, boolean value of allowfullscreen attribute.
+// sandbox: optional boolean. If true, the frame will be sandboxed (with
+// allow-scripts, so that tests can run in it.)
+function test_frame_policy(
+ feature, src, srcdoc, test_expect, allow, allowfullscreen, sandbox) {
+ let frame = document.createElement('iframe');
+ document.body.appendChild(frame);
+ // frame_policy should be dynamically updated as allow and allowfullscreen is
+ // updated.
+ var frame_policy = frame.permissionsPolicy;
+ if (typeof allow !== 'undefined') {
+ frame.setAttribute('allow', allow);
+ }
+ if (!!allowfullscreen) {
+ frame.setAttribute('allowfullscreen', true);
+ }
+ if (!!sandbox) {
+ frame.setAttribute('sandbox', 'allow-scripts');
+ }
+ if (!!src) {
+ frame.src = src;
+ }
+ if (!!srcdoc) {
+ frame.srcdoc = "<h1>Hello world!</h1>";
+ }
+ if (test_expect) {
+ assert_true(frame_policy.allowedFeatures().includes(feature));
+ } else {
+ assert_false(frame_policy.allowedFeatures().includes(feature));
+ }
+}
+
+function expect_reports(report_count, policy_name, description) {
+ async_test(t => {
+ var num_received_reports = 0;
+ new ReportingObserver(t.step_func((reports, observer) => {
+ const relevant_reports = reports.filter(r => (r.body.featureId === policy_name));
+ num_received_reports += relevant_reports.length;
+ if (num_received_reports >= report_count) {
+ t.done();
+ }
+ }), {types: ['permissions-policy-violation'], buffered: true}).observe();
+ }, description);
+}
diff --git a/testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js b/testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js
new file mode 100644
index 0000000000..1bf3c1c12a
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/picture-in-picture.js
@@ -0,0 +1,30 @@
+function async_pip_test(func, name) {
+ async_test(t => {
+ assert_true('pictureInPictureEnabled' in document, 'Picture-in-Picture API is available');
+ func(t);
+ }, name);
+}
+
+function promise_pip_test(func, name) {
+ promise_test(async t => {
+ assert_true('pictureInPictureEnabled' in document, 'Picture-in-Picture API is available');
+ return func(t);
+ }, name);
+}
+
+function isPictureInPictureAllowed() {
+ return new Promise(resolve => {
+ let video = document.createElement('video');
+ video.src = getVideoURI('/media/movie_5');
+ video.onloadedmetadata = () => {
+ video.requestPictureInPicture()
+ .then(() => resolve(document.pictureInPictureEnabled))
+ .catch(e => {
+ if (e.name == 'NotAllowedError')
+ resolve(document.pictureInPictureEnabled);
+ else
+ resolve(false);
+ });
+ };
+ });
+}
diff --git a/testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html b/testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html
new file mode 100644
index 0000000000..54d3cf55b1
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/redirect-on-load.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<body>
+<script>
+// Automatically redirects the page to a new URL on load.
+// Load this document with a URL like:
+// "permissions-policy/resources/redirect-on-load.html#https://www.example.com/"
+window.onload = function () {
+ document.location = document.location.hash.substring(1);
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html
new file mode 100644
index 0000000000..8240de99c6
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Return fullscreen permissions policy state from self and a sandboxed child frame</title>
+<script>
+ "use strict";
+ window.onload = () => {
+ let frame = document.createElement('iframe');
+ frame.src = "/permissions-policy/resources/nested-sandbox.html";
+ frame.sandbox = "allow-scripts";
+
+ var handle_message = evt => {
+ if (evt.source === frame.contentWindow) {
+ window.parent.postMessage({
+ "child": document.featurePolicy.allowedFeatures().includes("fullscreen"),
+ "grandchild": evt.data
+ },"*");
+ document.body.removeChild(frame);
+ window.removeEventListener('message', handle_message);
+ }
+ };
+ window.addEventListener('message', handle_message);
+ document.body.appendChild(frame);
+ };
+</script>
diff --git a/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers
new file mode 100644
index 0000000000..ff7ae41353
--- /dev/null
+++ b/testing/web-platform/tests/permissions-policy/resources/sandbox-self.html.headers
@@ -0,0 +1 @@
+Permissions-Policy: fullscreen=self