Reland "Reject Web Serial requests with an opaque origin"
This reverts commit cd2f352906cbfa254100390271e51e9890ed2bbe.
Reason for revert: The fixed parent CL https://chromium-review.googlesource.com/c/chromium/src/+/4307819 is re-landed.
Original change's description:
> Revert "Reject Web Serial requests with an opaque origin"
>
> Revert submission 4112689
>
> Reason for revert: suspect for introducing test failures for
> DedicatedWorkerTest.TopLevelFrameSecurityOrigin, for example
> https://ci.chromium.org/ui/p/chromium/builders/ci/linux-ubsan-vptr/21391/overview
>
> Reverted changes: /q/submissionid:4112689
>
> Change-Id: I496ec21cc6a6e39dd3c2943b8fc7ce3d179a1f73
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4307114
> Owners-Override: Mikel Astiz <mastiz@google.com>
> Commit-Queue: Mikel Astiz <mastiz@chromium.org>
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> Cr-Commit-Position: refs/heads/main@{#1112768}
Change-Id: Ic3f4fe58a946b53a23dfba57d30ee889ec3cc359
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4311852
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Jack Hsieh <chengweih@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1113766}
diff --git a/serial/getPorts/reject_opaque_origin.https.html b/serial/getPorts/reject_opaque_origin.https.html
new file mode 100644
index 0000000..b2f630a
--- /dev/null
+++ b/serial/getPorts/reject_opaque_origin.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ 'use strict';
+
+ promise_test(async (t) => {
+ await promise_rejects_dom(
+ t, 'SecurityError', navigator.serial.getPorts(),
+ 'getPorts() should throw a SecurityError DOMException when called ' +
+ 'from a context where the top-level document has an opaque origin.');
+ }, 'Calls to Serial APIs from an origin with opaque top origin get blocked.');
+</script>
diff --git a/serial/getPorts/reject_opaque_origin.https.html.headers b/serial/getPorts/reject_opaque_origin.https.html.headers
new file mode 100644
index 0000000..1efcf8c
--- /dev/null
+++ b/serial/getPorts/reject_opaque_origin.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts
diff --git a/serial/getPorts/sandboxed_iframe.https.window.js b/serial/getPorts/sandboxed_iframe.https.window.js
new file mode 100644
index 0000000..8fae11c
--- /dev/null
+++ b/serial/getPorts/sandboxed_iframe.https.window.js
@@ -0,0 +1,22 @@
+'use strict';
+
+let iframe = document.createElement('iframe');
+
+promise_test(async () => {
+ await new Promise(resolve => {
+ iframe.src = '../resources/open-in-iframe.html';
+ iframe.sandbox.add('allow-scripts');
+ iframe.allow = 'serial';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('load', resolve);
+ });
+
+ await new Promise(resolve => {
+ iframe.contentWindow.postMessage({type: 'GetPorts'}, '*');
+
+ window.addEventListener('message', (messageEvent) => {
+ assert_equals('Success', messageEvent.data);
+ resolve();
+ });
+ });
+}, 'GetPorts from a sandboxed iframe is valid.');
diff --git a/serial/requestPort/reject_opaque_origin.https.html b/serial/requestPort/reject_opaque_origin.https.html
new file mode 100644
index 0000000..ade8ae7
--- /dev/null
+++ b/serial/requestPort/reject_opaque_origin.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ 'use strict';
+
+ promise_test(async (t) => {
+ await promise_rejects_dom(
+ t, 'SecurityError', navigator.serial.requestPort(),
+ 'requestPort() should throw a SecurityError DOMException when called ' +
+ 'from a context where the top-level document has an opaque origin.');
+ }, 'Calls to Serial APIs from an origin with opaque top origin get blocked.');
+</script>
diff --git a/serial/requestPort/reject_opaque_origin.https.html.headers b/serial/requestPort/reject_opaque_origin.https.html.headers
new file mode 100644
index 0000000..1efcf8c
--- /dev/null
+++ b/serial/requestPort/reject_opaque_origin.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts
diff --git a/serial/requestPort/sandboxed_iframe.https.window.js b/serial/requestPort/sandboxed_iframe.https.window.js
new file mode 100644
index 0000000..6e16951
--- /dev/null
+++ b/serial/requestPort/sandboxed_iframe.https.window.js
@@ -0,0 +1,26 @@
+'use strict';
+
+let iframe = document.createElement('iframe');
+
+promise_test(async () => {
+ await new Promise(resolve => {
+ iframe.src = '../resources/open-in-iframe.html';
+ iframe.sandbox.add('allow-scripts');
+ iframe.allow = 'serial';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('load', resolve);
+ });
+
+ await new Promise(resolve => {
+ iframe.contentWindow.postMessage({type: 'RequestPort'}, '*');
+
+ window.addEventListener('message', (messageEvent) => {
+ // The failure message of no device chosen is expected. The point here is
+ // to validate not failing because of a sandboxed iframe.
+ assert_equals(
+ 'FAIL: NotFoundError: Failed to execute \'requestPort\' on \'Serial\': No port selected by the user.',
+ messageEvent.data);
+ resolve();
+ });
+ });
+}, 'RequestPort from a sandboxed iframe is valid.');
diff --git a/serial/resources/open-in-iframe.html b/serial/resources/open-in-iframe.html
new file mode 100644
index 0000000..9bf8beb
--- /dev/null
+++ b/serial/resources/open-in-iframe.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+ <button>Fake user gesture</button>
+</body>
+
+<script>
+ 'use strict';
+
+ test_driver.set_test_context(parent);
+
+ window.onmessage = messageEvent => {
+ switch (messageEvent.data.type) {
+ case 'GetPorts':
+ navigator.serial.getPorts()
+ .then(ports => parent.postMessage('Success', '*'))
+ .catch(err => parent.postMessage(`FAIL: ${err}`, '*'));
+ break;
+ case 'RequestPort':
+ test_driver.click(document.getElementsByTagName('button')[0])
+ .then(() => navigator.serial.requestPort({filters: []}))
+ .then(port => parent.postMessage('Success', '*'))
+ .catch(err => parent.postMessage(`FAIL: ${err}`, '*'));
+ break;
+ default:
+ parent.postMessage(
+ `FAIL: Bad message type: ${messageEvent.data}`, '*');
+ };
+ };
+</script>