summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webusb/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/webusb/resources
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webusb/resources')
-rw-r--r--testing/web-platform/tests/webusb/resources/fake-devices.js175
-rw-r--r--testing/web-platform/tests/webusb/resources/manual.js110
-rw-r--r--testing/web-platform/tests/webusb/resources/open-in-iframe.html42
-rw-r--r--testing/web-platform/tests/webusb/resources/open-in-worker.js16
-rw-r--r--testing/web-platform/tests/webusb/resources/usb-allowed-by-permissions-policy-worker.js14
-rw-r--r--testing/web-platform/tests/webusb/resources/usb-disabled-by-permissions-policy-worker.js17
-rw-r--r--testing/web-platform/tests/webusb/resources/usb-helpers.js104
7 files changed, 478 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webusb/resources/fake-devices.js b/testing/web-platform/tests/webusb/resources/fake-devices.js
new file mode 100644
index 0000000000..c5c5cadaa6
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/fake-devices.js
@@ -0,0 +1,175 @@
+'use strict';
+
+let fakeDeviceInit = {
+ usbVersionMajor: 2,
+ usbVersionMinor: 0,
+ usbVersionSubminor: 0,
+ deviceClass: 7,
+ deviceSubclass: 1,
+ deviceProtocol: 2,
+ vendorId: 0x18d1,
+ productId: 0xf00d,
+ deviceVersionMajor: 1,
+ deviceVersionMinor: 2,
+ deviceVersionSubminor: 3,
+ manufacturerName: 'Google, Inc.',
+ productName: 'The amazing imaginary printer',
+ serialNumber: '4',
+ activeConfigurationValue: 0,
+ configurations: [
+ {
+ configurationValue: 1,
+ configurationName: 'Printer Mode',
+ interfaces: [
+ {
+ interfaceNumber: 0,
+ alternates: [{
+ alternateSetting: 0,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x01,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Control',
+ endpoints: [{
+ endpointNumber: 1,
+ direction: 'in',
+ type: 'interrupt',
+ packetSize: 8
+ }]
+ }]
+ },
+ {
+ interfaceNumber: 1,
+ alternates: [{
+ alternateSetting: 0,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x02,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Data',
+ endpoints: [
+ {
+ endpointNumber: 2,
+ direction: 'in',
+ type: 'bulk',
+ packetSize: 1024
+ },
+ {
+ endpointNumber: 2,
+ direction: 'out',
+ type: 'bulk',
+ packetSize: 1024
+ }
+ ]
+ }]
+ }
+ ]
+ },
+ {
+ configurationValue: 2,
+ configurationName: 'Fighting Robot Mode',
+ interfaces: [{
+ interfaceNumber: 0,
+ alternates: [
+ {
+ alternateSetting: 0,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x42,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Disabled',
+ endpoints: []
+ },
+ {
+ alternateSetting: 1,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x42,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Activate!',
+ endpoints: [
+ {
+ endpointNumber: 1,
+ direction: 'in',
+ type: 'isochronous',
+ packetSize: 1024
+ },
+ {
+ endpointNumber: 1,
+ direction: 'out',
+ type: 'isochronous',
+ packetSize: 1024
+ }
+ ]
+ }
+ ]
+ }]
+ },
+ {
+ configurationValue: 3,
+ configurationName: 'Non-sequential interface number and alternate ' +
+ 'setting Mode',
+ interfaces: [
+ {
+ interfaceNumber: 0,
+ alternates: [
+ {
+ alternateSetting: 0,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x01,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Control',
+ endpoints: [{
+ endpointNumber: 1,
+ direction: 'in',
+ type: 'interrupt',
+ packetSize: 8
+ }]
+ },
+ {
+ alternateSetting: 2,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x02,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Data',
+ endpoints: [
+ {
+ endpointNumber: 2,
+ direction: 'in',
+ type: 'bulk',
+ packetSize: 1024
+ },
+ {
+ endpointNumber: 2,
+ direction: 'out',
+ type: 'bulk',
+ packetSize: 1024
+ }
+ ]
+ }
+ ]
+ },
+ {
+ interfaceNumber: 2,
+ alternates: [{
+ alternateSetting: 0,
+ interfaceClass: 0xff,
+ interfaceSubclass: 0x02,
+ interfaceProtocol: 0x01,
+ interfaceName: 'Data',
+ endpoints: [
+ {
+ endpointNumber: 2,
+ direction: 'in',
+ type: 'bulk',
+ packetSize: 1024
+ },
+ {
+ endpointNumber: 2,
+ direction: 'out',
+ type: 'bulk',
+ packetSize: 1024
+ }
+ ]
+ }]
+ }
+ ]
+ }
+ ]
+};
diff --git a/testing/web-platform/tests/webusb/resources/manual.js b/testing/web-platform/tests/webusb/resources/manual.js
new file mode 100644
index 0000000000..e8dc08a8bd
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/manual.js
@@ -0,0 +1,110 @@
+let manualTestDevice = null;
+
+navigator.usb.addEventListener('disconnect', (e) => {
+ if (e.device === manualTestDevice) {
+ manualTestDevice = null;
+ }
+})
+
+async function getDeviceForManualTest() {
+ if (manualTestDevice) {
+ return manualTestDevice;
+ }
+
+ const button = document.createElement('button');
+ button.textContent = 'Click to select a device';
+ button.style.display = 'block';
+ button.style.fontSize = '20px';
+ button.style.padding = '10px';
+
+ await new Promise((resolve) => {
+ button.onclick = () => {
+ document.body.removeChild(button);
+ resolve();
+ };
+ document.body.appendChild(button);
+ });
+
+ manualTestDevice = await navigator.usb.requestDevice({filters: []});
+ assert_true(manualTestDevice instanceof USBDevice);
+
+ return manualTestDevice;
+}
+
+function manual_usb_test(func, name, properties) {
+ promise_test(async (test) => {
+ await func(test, await getDeviceForManualTest());
+ }, name, properties);
+}
+
+function manual_usb_serial_test(func, name, properties) {
+ promise_test(async (test) => {
+ const device = await getDeviceForManualTest();
+ await device.open();
+ test.add_cleanup(async () => {
+ if (device.opened) {
+ await device.close();
+ }
+ });
+
+ await device.selectConfiguration(1);
+
+ let controlInterface = undefined;
+ for (const iface of device.configuration.interfaces) {
+ const alternate = iface.alternates[0];
+ if (alternate.interfaceClass == 2 &&
+ alternate.interfaceSubclass == 2 &&
+ alternate.interfaceProtocol == 0) {
+ controlInterface = iface;
+ break;
+ }
+ }
+ assert_not_equals(controlInterface, undefined,
+ 'No control interface found.');
+
+ let dataInterface = undefined;
+ for (const iface of device.configuration.interfaces) {
+ const alternate = iface.alternates[0];
+ if (alternate.interfaceClass == 10 &&
+ alternate.interfaceSubclass == 0 &&
+ alternate.interfaceProtocol == 0) {
+ dataInterface = iface;
+ break;
+ }
+ }
+ assert_not_equals(dataInterface, undefined, 'No data interface found.');
+
+ await device.claimInterface(controlInterface.interfaceNumber);
+ await device.claimInterface(dataInterface.interfaceNumber);
+
+ let inEndpoint = undefined;
+ for (const endpoint of dataInterface.alternate.endpoints) {
+ if (endpoint.type == 'bulk' && endpoint.direction == 'in') {
+ inEndpoint = endpoint;
+ break;
+ }
+ }
+ assert_not_equals(inEndpoint, undefined, 'No IN endpoint found.');
+
+ let outEndpoint = undefined;
+ for (const endpoint of dataInterface.alternate.endpoints) {
+ if (endpoint.type == 'bulk' && endpoint.direction == 'out') {
+ outEndpoint = endpoint;
+ break;
+ }
+ }
+ assert_not_equals(outEndpoint, undefined, 'No OUT endpoint found.');
+
+ // Execute a SET_CONTROL_LINE_STATE command to let the device know the
+ // host is ready to transmit and receive data.
+ await device.controlTransferOut({
+ requestType: 'class',
+ recipient: 'interface',
+ request: 0x22,
+ value: 0x01,
+ index: controlInterface.interfaceNumber,
+ });
+
+ await func(test, device, inEndpoint, outEndpoint);
+ }, name, properties);
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/webusb/resources/open-in-iframe.html b/testing/web-platform/tests/webusb/resources/open-in-iframe.html
new file mode 100644
index 0000000000..ad0e12d371
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/open-in-iframe.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!-- Intentionally use relative paths here because this file is also used by blink/web_tests/usb/usbDevice-iframe.html. -->
+<script src="../../resources/test-only-api.js"></script>
+<script src="usb-helpers.js"></script>
+<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) {
+ case 'ConnectEvent':
+ navigator.usb.addEventListener('connect', connectEvent => {
+ connectEvent.device.open()
+ .then(() => parent.postMessage('Success', '*'))
+ .catch(err =>
+ parent.postMessage(`FAIL: open rejected ${err}`, '*'));
+ });
+ parent.postMessage('Ready', '*');
+ break;
+ case 'GetDevices':
+ navigator.usb.getDevices()
+ .then(devices => parent.postMessage('Success', '*'))
+ .catch(err => parent.postMessage(`FAIL: ${err}`, '*'));
+ break;
+ case 'RequestDevice':
+ test_driver.click(document.getElementsByTagName('button')[0])
+ .then(() => navigator.usb.requestDevice({filters: []}))
+ .then(device => parent.postMessage('Success', '*'))
+ .catch(err => parent.postMessage(`FAIL: ${err}`, '*'));
+ break;
+ default:
+ parent.postMessage(
+ `FAIL: Bad message type: ${messageEvent.data}`, '*');
+ }
+ };
+</script>
diff --git a/testing/web-platform/tests/webusb/resources/open-in-worker.js b/testing/web-platform/tests/webusb/resources/open-in-worker.js
new file mode 100644
index 0000000000..2175cfd397
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/open-in-worker.js
@@ -0,0 +1,16 @@
+importScripts('/resources/test-only-api.js');
+importScripts('/webusb/resources/usb-helpers.js');
+'use strict';
+
+onmessage = messageEvent => {
+ if (messageEvent.data.type === 'ConnectEvent') {
+ navigator.usb.addEventListener('connect', connectEvent => {
+ connectEvent.device.open().then(() => {
+ postMessage({ type: 'Success' });
+ }).catch(error => {
+ postMessage({ type: `FAIL: open rejected ${error}` });
+ });
+ });
+ postMessage({ type: 'Ready' });
+ }
+};
diff --git a/testing/web-platform/tests/webusb/resources/usb-allowed-by-permissions-policy-worker.js b/testing/web-platform/tests/webusb/resources/usb-allowed-by-permissions-policy-worker.js
new file mode 100644
index 0000000000..d06a586474
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/usb-allowed-by-permissions-policy-worker.js
@@ -0,0 +1,14 @@
+'use strict';
+
+importScripts('/resources/testharness.js');
+
+let workerType;
+
+if (typeof postMessage === 'function') {
+ workerType = 'dedicated';
+}
+
+promise_test(() => navigator.usb.getDevices(),
+ `Inherited header permissions policy allows ${workerType} workers.`);
+
+done();
diff --git a/testing/web-platform/tests/webusb/resources/usb-disabled-by-permissions-policy-worker.js b/testing/web-platform/tests/webusb/resources/usb-disabled-by-permissions-policy-worker.js
new file mode 100644
index 0000000000..caf2727cd1
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/usb-disabled-by-permissions-policy-worker.js
@@ -0,0 +1,17 @@
+'use strict';
+
+importScripts('/resources/testharness.js');
+
+const header = 'Permissions-Policy header usb=()';
+let workerType;
+
+if (typeof postMessage === 'function') {
+ workerType = 'dedicated';
+}
+
+promise_test(() => navigator.usb.getDevices().then(
+ () => assert_unreached('expected promise to reject with SecurityError'),
+ error => assert_equals(error.name, 'SecurityError')),
+ `Inherited ${header} disallows ${workerType} workers.`);
+
+done();
diff --git a/testing/web-platform/tests/webusb/resources/usb-helpers.js b/testing/web-platform/tests/webusb/resources/usb-helpers.js
new file mode 100644
index 0000000000..cb6aaadf98
--- /dev/null
+++ b/testing/web-platform/tests/webusb/resources/usb-helpers.js
@@ -0,0 +1,104 @@
+'use strict';
+
+// These tests rely on the User Agent providing an implementation of the
+// WebUSB Testing API (https://wicg.github.io/webusb/test/).
+//
+// In Chromium-based browsers this implementation is provided by a polyfill
+// in order to reduce the amount of test-only code shipped to users. To enable
+// these tests the browser must be run with these options:
+//
+// --enable-blink-features=MojoJS,MojoJSTest
+
+(() => {
+ // Load scripts needed by the test API on context creation.
+ if (isChromiumBased) {
+ loadScript('/resources/chromium/webusb-child-test.js');
+ }
+})();
+
+function usb_test(func, name, properties) {
+ promise_test(async (t) => {
+ assert_implements(navigator.usb, 'missing navigator.usb');
+ if (navigator.usb.test === undefined) {
+ // Try loading a polyfill for the WebUSB Testing API.
+ if (isChromiumBased) {
+ await loadScript('/resources/chromium/webusb-test.js');
+ }
+ }
+ assert_implements(navigator.usb.test, 'missing navigator.usb.test after initialization');
+
+ await navigator.usb.test.initialize();
+ try {
+ await func(t);
+ } finally {
+ await navigator.usb.test.reset();
+ }
+ }, name, properties);
+}
+
+// Returns a promise that is resolved when the next USBConnectionEvent of the
+// given type is received.
+function connectionEventPromise(eventType) {
+ return new Promise(resolve => {
+ let eventHandler = e => {
+ assert_true(e instanceof USBConnectionEvent);
+ navigator.usb.removeEventListener(eventType, eventHandler);
+ resolve(e.device);
+ };
+ navigator.usb.addEventListener(eventType, eventHandler);
+ });
+}
+
+// Creates a fake device and returns a promise that resolves once the
+// 'connect' event is fired for the fake device. The promise is resolved with
+// an object containing the fake USB device and the corresponding USBDevice.
+function getFakeDevice() {
+ let promise = connectionEventPromise('connect');
+ let fakeDevice = navigator.usb.test.addFakeDevice(fakeDeviceInit);
+ return promise.then(device => {
+ return { device: device, fakeDevice: fakeDevice };
+ });
+}
+
+// Disconnects the given device and returns a promise that is resolved when it
+// is done.
+function waitForDisconnect(fakeDevice) {
+ let promise = connectionEventPromise('disconnect');
+ fakeDevice.disconnect();
+ return promise;
+}
+
+function assertDeviceInfoEquals(usbDevice, deviceInit) {
+ for (var property in deviceInit) {
+ if (property == 'activeConfigurationValue') {
+ if (deviceInit.activeConfigurationValue == 0) {
+ assert_equals(usbDevice.configuration, null);
+ } else {
+ assert_equals(usbDevice.configuration.configurationValue,
+ deviceInit.activeConfigurationValue);
+ }
+ } else if (Array.isArray(deviceInit[property])) {
+ assert_equals(usbDevice[property].length, deviceInit[property].length);
+ for (var i = 0; i < usbDevice[property].length; ++i)
+ assertDeviceInfoEquals(usbDevice[property][i], deviceInit[property][i]);
+ } else {
+ assert_equals(usbDevice[property], deviceInit[property], property);
+ }
+ }
+}
+
+function callWithTrustedClick(callback) {
+ return new Promise(resolve => {
+ let button = document.createElement('button');
+ button.textContent = 'click to continue test';
+ button.style.display = 'block';
+ button.style.fontSize = '20px';
+ button.style.padding = '10px';
+ button.onclick = () => {
+ resolve(callback());
+ document.body.removeChild(button);
+ };
+ document.body.appendChild(button);
+ test_driver.click(button);
+ });
+}