summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/bluetooth/requestDevice
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/bluetooth/requestDevice
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/bluetooth/requestDevice')
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.window.js19
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.js21
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.js25
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/blocklisted-manufacturer-data-in-filter.https.window.js29
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js21
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.js29
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js22
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js33
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.js16
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-dataPrefix.https.window.js16
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filter.https.window.js14
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filters-member.https.window.js19
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.js12
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.window.js16
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js35
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.window.js33
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.window.js18
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/exclusion-filters-require-filters.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.window.js26
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js36
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.window.js20
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.window.js15
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.window.js15
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.window.js12
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js23
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.window.js18
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.window.js18
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.window.js37
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/cross-origin-iframe.sub.https.window.js28
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/discovery-succeeds.https.window.js31
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/doesnt-consume-user-gesture.https.window.js24
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/filter-matches.https.window.js76
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/le-not-supported.https.window.js15
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js139
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.window.js14
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/not-processing-user-gesture.https.window.js18
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/radio-not-present.https.window.js17
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html13
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html.headers1
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/request-from-iframe.https.window.js43
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.js35
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/same-device.https.window.js19
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/sandboxed_iframe.https.window.js27
-rw-r--r--testing/web-platform/tests/bluetooth/requestDevice/single-filter-single-service.https.window.js14
52 files changed, 1282 insertions, 0 deletions
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.window.js
new file mode 100644
index 0000000000..15bde6a933
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Device with empty name and no UUIDs nearby. Should be ' +
+ 'found if acceptAllDevices is true.';
+
+bluetooth_test(async () => {
+ let { device } = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {
+ name: ''
+ },
+ requestDeviceOptions: {
+ acceptAllDevices: true
+ }
+ });
+ assert_equals(device.name, '');
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.js
new file mode 100644
index 0000000000..f3373a6bb6
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+const test_desc =
+ 'A device with name and no UUIDs nearby. Should be found if ' +
+ 'acceptAllDevices is true.';
+const name = 'LE Device';
+
+bluetooth_test(async () => {
+ let { device } = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {
+ name: name
+ },
+ requestDeviceOptions: {
+ acceptAllDevices: true
+ }
+ });
+ assert_equals(device.name, name);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.js
new file mode 100644
index 0000000000..5226a645a8
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice called with acceptAllDevices: true and ' +
+ 'with no optionalServices. Should not get access to any services.';
+const expected = new DOMException(
+ 'Origin is not allowed to access any service. ' +
+ 'Tip: Add the service UUID to \'optionalServices\' in ' +
+ 'requestDevice() options. https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(
+ async () => {
+ let { device } = await getConnectedHealthThermometerDevice(
+ { acceptAllDevices: true });
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices(), expected);
+ },
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.js
new file mode 100644
index 0000000000..7c200d03f1
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice called with acceptAllDevices: true and with ' +
+ 'optionalServices. Should get access to services.';
+
+bluetooth_test(
+ async () => {
+ await getTwoHealthThermometerServicesDevice()
+ let device = await requestDeviceWithTrustedClick({
+ acceptAllDevices: true,
+ optionalServices: ['health_thermometer']
+ });
+ let gattServer = await device.gatt.connect();
+ let services = await gattServer.getPrimaryServices();
+ assert_equals(services.length, 2);
+ services.forEach(service => {
+ assert_equals(
+ service.uuid,
+ BluetoothUUID.getService('health_thermometer'));
+ });
+ },
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-manufacturer-data-in-filter.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-manufacturer-data-in-filter.https.window.js
new file mode 100644
index 0000000000..2dae7f4cc6
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-manufacturer-data-in-filter.https.window.js
@@ -0,0 +1,29 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Reject with SecurityError if requesting a blocklisted ' +
+ 'manufacturer data.';
+
+const expected = new DOMException(
+ 'requestDevice() called with a filter containing a blocklisted UUID ' +
+ 'or manufacturer data. https://goo.gl/4NeimX',
+ 'SecurityError');
+
+bluetooth_test(async () => {
+ await assert_promise_rejects_with_message(
+ setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {knownServiceUUIDs: ['heart_rate']},
+ requestDeviceOptions: {
+ filters: [{
+ services: ['heart_rate'],
+ manufacturerData: [{
+ companyIdentifier: blocklistedManufacturerId,
+ dataPrefix: blocklistedManufacturerData,
+ }],
+ }]
+ }
+ }),
+ expected, 'Requesting blocklisted service rejects.');
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js
new file mode 100644
index 0000000000..80eaf14447
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-filter.https.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Reject with SecurityError if requesting a blocklisted ' +
+ 'service.';
+const expected = new DOMException(
+ 'requestDevice() called with a filter containing a blocklisted UUID ' +
+ 'or manufacturer data. https://goo.gl/4NeimX',
+ 'SecurityError');
+
+bluetooth_test(async () => {
+ await assert_promise_rejects_with_message(
+ setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {knownServiceUUIDs: ['human_interface_device']},
+ requestDeviceOptions:
+ {filters: [{services: ['human_interface_device']}]}
+ }),
+ expected, 'Requesting blocklisted service rejects.');
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.js
new file mode 100644
index 0000000000..4c01974e55
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.js
@@ -0,0 +1,29 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Blocklisted UUID in optionalServices is removed and ' +
+ 'access not granted.';
+const expected = new DOMException(
+ 'Origin is not allowed to access the ' +
+ 'service. Tip: Add the service UUID to \'optionalServices\' in ' +
+ 'requestDevice() options. https://goo.gl/HxfxSQ',
+ 'SecurityError');
+
+bluetooth_test(async () => {
+ let {device, fake_peripheral} = await getDiscoveredHealthThermometerDevice({
+ filters: [{services: ['health_thermometer']}],
+ optionalServices: ['human_interface_device']
+ });
+ await fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS});
+ await device.gatt.connect();
+ Promise.all([
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryService('human_interface_device'), expected,
+ 'Blocklisted service not accessible.'),
+ assert_promise_rejects_with_message(
+ device.gatt.getPrimaryServices('human_interface_device'), expected,
+ 'Blocklisted services not accessible.')
+ ])
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js
new file mode 100644
index 0000000000..fa2645093a
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js
@@ -0,0 +1,22 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc =
+ 'Manufacturer data mask size must be equal to dataPrefix size.';
+
+bluetooth_test(async (t) => {
+ const companyIdentifier = 0x0001;
+ const dataPrefix = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
+ const mask = new Uint8Array([0xff]);
+
+ await promise_rejects_js(
+ t, TypeError,
+ requestDeviceWithTrustedClick(
+ {filters: [{manufacturerData: [{companyIdentifier, mask}]}]}));
+ await promise_rejects_js(
+ t, TypeError, requestDeviceWithTrustedClick({
+ filters: [{manufacturerData: [{companyIdentifier, dataPrefix, mask}]}]
+ }));
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js
new file mode 100644
index 0000000000..936ca4735c
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js
@@ -0,0 +1,33 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'dataPrefix value buffer must not be detached';
+
+function detachBuffer(buffer) {
+ window.postMessage('', '*', [buffer]);
+}
+
+bluetooth_test(async (t) => {
+ const companyIdentifier = 0x0001;
+
+ const typed_array = Uint8Array.of(1, 2);
+ detachBuffer(typed_array.buffer);
+
+ await promise_rejects_dom(
+ t, 'InvalidStateError', requestDeviceWithTrustedClick({
+ filters:
+ [{manufacturerData: [{companyIdentifier, dataPrefix: typed_array}]}]
+ }));
+
+ const array_buffer = Uint8Array.of(3, 4).buffer;
+ detachBuffer(array_buffer);
+
+ await promise_rejects_dom(
+ t, 'InvalidStateError', requestDeviceWithTrustedClick({
+ filters: [
+ {manufacturerData: [{companyIdentifier, dataPrefix: array_buffer}]}
+ ]
+ }));
+}, test_desc); \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.js
new file mode 100644
index 0000000000..20ed383d39
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.js
@@ -0,0 +1,16 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A device name between 29 and 248 bytes is valid.';
+const DEVICE_NAME = 'a_device_name_that_is_longer_than_29_bytes_but_' +
+ 'shorter_than_248_bytes';
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: DEVICE_NAME},
+ requestDeviceOptions: {filters: [{name: DEVICE_NAME}]}
+ });
+ assert_equals(device.name, DEVICE_NAME)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-dataPrefix.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-dataPrefix.https.window.js
new file mode 100644
index 0000000000..75e12219cc
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-dataPrefix.https.window.js
@@ -0,0 +1,16 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'dataPrefix when present must be non-empty';
+
+bluetooth_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError, requestDeviceWithTrustedClick({
+ filters: [{
+ manufacturerData:
+ [{companyIdentifier: 1, dataPrefix: new Uint8Array()}]
+ }]
+ }));
+}, test_desc); \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filter.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filter.https.window.js
new file mode 100644
index 0000000000..0d4b196cc7
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filter.https.window.js
@@ -0,0 +1,14 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'An exclusion filter must restrict the devices in some way.';
+const expected = new TypeError();
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(
+ {filters: [{name: 'Name'}], exclusionFilters: [{}]}),
+ expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filters-member.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filters-member.https.window.js
new file mode 100644
index 0000000000..d380fa0268
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filters-member.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc =
+ 'An empty |exclusionFilters| member should result in a TypeError';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on ' +
+ '\'Bluetooth\': \'exclusionFilters\' member must be non-empty to ' +
+ 'exclude any device.',
+ new TypeError());
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(
+ {filters: [{name: 'Name'}], exclusionFilters: []}),
+ expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.js
new file mode 100644
index 0000000000..bfe94f2721
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.js
@@ -0,0 +1,12 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A filter must restrict the devices in some way.';
+const expected = new TypeError();
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({filters: [{}]}), expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.window.js
new file mode 100644
index 0000000000..3265e54fd8
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.window.js
@@ -0,0 +1,16 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'An empty |filters| member should result in a TypeError';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on ' +
+ '\'Bluetooth\': \'filters\' member must be non-empty to ' +
+ 'find any devices.',
+ new TypeError());
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({filters: []}), expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js
new file mode 100644
index 0000000000..0996137f51
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js
@@ -0,0 +1,35 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice with empty manufacturerData. ' +
+ 'Should reject with TypeError.';
+const test_specs = [
+ {filters: [{manufacturerData: []}]},
+ {filters: [{manufacturerData: [], name: 'Name'}]},
+ {filters: [{manufacturerData: [], services: ['heart_rate']}]},
+ {filters: [{manufacturerData: [], name: 'Name', services: ['heart_rate']}]},
+ {filters: [{manufacturerData: []}], optionalServices: ['heart_rate']}, {
+ filters: [{manufacturerData: [], name: 'Name'}],
+ optionalServices: ['heart_rate']
+ },
+ {
+ filters: [{manufacturerData: [], services: ['heart_rate']}],
+ optionalServices: ['heart_rate']
+ },
+ {
+ filters: [{manufacturerData: [], name: 'Name', services: ['heart_rate']}],
+ optionalServices: ['heart_rate']
+ }
+];
+
+bluetooth_test((t) => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {
+ test_promises = test_promises.then(
+ () => promise_rejects_js(
+ t, TypeError, requestDeviceWithTrustedClick(args)));
+ });
+ return test_promises;
+}, test_desc); \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.window.js
new file mode 100644
index 0000000000..8ce2e64967
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.window.js
@@ -0,0 +1,33 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice with empty namePrefix. ' +
+ 'Should reject with TypeError.';
+const expected = new TypeError();
+const test_specs = [
+ {filters: [{namePrefix: ''}]}, {filters: [{namePrefix: '', name: 'Name'}]},
+ {filters: [{namePrefix: '', services: ['heart_rate']}]},
+ {filters: [{namePrefix: '', name: 'Name', services: ['heart_rate']}]},
+ {filters: [{namePrefix: ''}], optionalServices: ['heart_rate']},
+ {filters: [{namePrefix: '', name: 'Name'}], optionalServices: ['heart_rate']},
+ {
+ filters: [{namePrefix: '', services: ['heart_rate']}],
+ optionalServices: ['heart_rate']
+ },
+ {
+ filters: [{namePrefix: '', name: 'Name', services: ['heart_rate']}],
+ optionalServices: ['heart_rate']
+ }
+];
+
+bluetooth_test(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {
+ test_promises = test_promises.then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(args), expected));
+ });
+ return test_promises;
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.window.js
new file mode 100644
index 0000000000..a24611631d
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.window.js
@@ -0,0 +1,18 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Services member must contain at least one service.';
+const expected = new TypeError();
+
+bluetooth_test(() => {
+ let test_promises = Promise.resolve();
+ generateRequestDeviceArgsWithServices([]).forEach(
+ args => {
+ test_promises = test_promises.then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(args), expected,
+ 'Services member must contain at least one service'))});
+ return test_promises;
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/exclusion-filters-require-filters.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/exclusion-filters-require-filters.https.window.js
new file mode 100644
index 0000000000..d7db260dee
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/exclusion-filters-require-filters.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc =
+ 'RequestDeviceOptions should have \'filters\' if \'exclusionFilters\' is present. Reject with TypeError if not.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+ '\'filters\' member must be present if \'exclusionFilters\' is present.',
+ new TypeError());
+const test_specs = [
+ {exclusionFilters: []},
+ {exclusionFilters: [], acceptAllDevices: true},
+ {exclusionFilters: [{}]},
+ {exclusionFilters: [{}], acceptAllDevices: true},
+ {exclusionFilters: [{name: 'Name'}]},
+ {exclusionFilters: [{name: 'Name'}], acceptAllDevices: true},
+];
+
+bluetooth_test(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {test_promises = test_promises.then(() => {
+ return assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(args), expected)
+ })});
+ return test_promises;
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.window.js
new file mode 100644
index 0000000000..a6c48f2962
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.window.js
@@ -0,0 +1,26 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'RequestDeviceOptions should have exactly one of ' +
+ '\'filters\' or \'acceptAllDevices:true\'. Reject with TypeError if not.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+ 'Either \'filters\' should be present or ' +
+ '\'acceptAllDevices\' should be true, but not both.',
+ new TypeError());
+const test_specs = [
+ {}, {optionalServices: ['heart_rate']}, {filters: [], acceptAllDevices: true},
+ {filters: [], acceptAllDevices: true, optionalServices: ['heart_rate']}
+];
+
+bluetooth_test(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(
+ args => {
+ test_promises = test_promises.then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(args), expected))});
+ return test_promises;
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js
new file mode 100644
index 0000000000..18cdbb4b4a
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'companyIdentifier must be in the [0, 65535] range';
+
+bluetooth_test(async (t) => {
+ await promise_rejects_js(
+ t, TypeError,
+ requestDeviceWithTrustedClick(
+ {filters: [{manufacturerData: [{companyIdentifier: -1}]}]}));
+ await promise_rejects_js(
+ t, TypeError,
+ requestDeviceWithTrustedClick(
+ {filters: [{manufacturerData: [{companyIdentifier: 65536}]}]}));
+}, test_desc); \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js
new file mode 100644
index 0000000000..502e2e4057
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js
@@ -0,0 +1,36 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'mask value buffer must not be detached';
+
+function detachBuffer(buffer) {
+ window.postMessage('', '*', [buffer]);
+}
+
+bluetooth_test(async (t) => {
+ const companyIdentifier = 0x0001;
+ const dataPrefix = Uint8Array.of(1, 2);
+
+ const typed_array = Uint8Array.of(1, 2);
+ detachBuffer(typed_array.buffer);
+
+ await promise_rejects_dom(
+ t, 'InvalidStateError', requestDeviceWithTrustedClick({
+ filters: [{
+ manufacturerData: [{companyIdentifier, dataPrefix, mask: typed_array}]
+ }]
+ }));
+
+ const array_buffer = Uint8Array.of(3, 4).buffer;
+ detachBuffer(array_buffer);
+
+ await promise_rejects_dom(
+ t, 'InvalidStateError', requestDeviceWithTrustedClick({
+ filters: [{
+ manufacturerData:
+ [{companyIdentifier, dataPrefix, mask: array_buffer}]
+ }]
+ }));
+}, test_desc); \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.window.js
new file mode 100644
index 0000000000..3458c92b65
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Unicode string with utf8 representation longer than 248 ' +
+ 'bytes in \'name\' must throw TypeError.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+ 'A device name can\'t be longer than 248 bytes.',
+ new TypeError());
+// \u2764's UTF-8 representation is 3 bytes long.
+// 83 chars * 3 bytes/char = 249 bytes
+const unicode_name = '\u2764'.repeat(83);
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({filters: [{name: unicode_name}]}),
+ expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.window.js
new file mode 100644
index 0000000000..f14f78fe7d
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A device name longer than 248 must reject.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': A device ' +
+ 'name can\'t be longer than 248 bytes.',
+ new TypeError());
+const name_too_long = 'a'.repeat(249);
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({filters: [{name: name_too_long}]}),
+ expected, 'Device name longer than 248'),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.window.js
new file mode 100644
index 0000000000..b2e6668e4b
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.window.js
@@ -0,0 +1,20 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Unicode string with utf8 representation longer than 248 ' +
+ 'bytes in \'namePrefix\' must throw NotFoundError.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+ 'A device name can\'t be longer than 248 bytes.',
+ new TypeError());
+// \u2764's UTF-8 representation is 3 bytes long.
+// 83 chars * 3 bytes/char = 249 bytes
+const unicode_name = '\u2764'.repeat(83);
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({filters: [{namePrefix: unicode_name}]}),
+ expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.window.js
new file mode 100644
index 0000000000..5d27629eaa
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A device name prefix longer than 248 must reject.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': A device ' +
+ 'name can\'t be longer than 248 bytes.',
+ new TypeError());
+const name_too_long = 'a'.repeat(249);
+
+bluetooth_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({filters: [{namePrefix: name_too_long}]}),
+ expected, 'Device name longer than 248'),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.window.js
new file mode 100644
index 0000000000..6a3cf5bead
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A unicode device name of 248 bytes is valid.';
+// \u00A1's UTF-8 representation is 2 bytes long.
+// 124 chars * 2 bytes/char = 248 bytes
+const DEVICE_NAME = '\u00A1'.repeat(124);
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: DEVICE_NAME},
+ requestDeviceOptions: {filters: [{name: DEVICE_NAME}]}
+ });
+ device => assert_equals(device.name, DEVICE_NAME)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.window.js
new file mode 100644
index 0000000000..7ede93ce72
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A device name of 248 bytes is valid.';
+const DEVICE_NAME = 'a'.repeat(248);
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: DEVICE_NAME},
+ requestDeviceOptions: {filters: [{name: DEVICE_NAME}]}
+ });
+ device => assert_equals(device.name, DEVICE_NAME)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.window.js
new file mode 100644
index 0000000000..2061e9863b
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A unicode device namePrefix of 248 bytes is valid.';
+// \u00A1's UTF-8 representation is 2 bytes long.
+// 124 chars * 2 bytes/char = 248 bytes
+const DEVICE_NAME = '\u00A1'.repeat(124);
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: DEVICE_NAME},
+ requestDeviceOptions: {filters: [{namePrefix: DEVICE_NAME}]}
+ });
+ device => assert_equals(device.name, DEVICE_NAME)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.window.js
new file mode 100644
index 0000000000..f922bb2f0d
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A device namePrefix of 248 bytes is valid.';
+const DEVICE_NAME = 'a'.repeat(248);
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: DEVICE_NAME},
+ requestDeviceOptions: {filters: [{namePrefix: DEVICE_NAME}]}
+ });
+ device => assert_equals(device.name, DEVICE_NAME)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.window.js
new file mode 100644
index 0000000000..075a97f1a9
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.window.js
@@ -0,0 +1,12 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice() requires an argument.';
+const expected = new TypeError();
+
+promise_test(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(), expected),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js
new file mode 100644
index 0000000000..41f851adc5
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js
@@ -0,0 +1,23 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Manufacturer data company identifier must be unique.';
+const expected = new TypeError();
+
+let filters = [{
+ manufacturerData: [
+ {
+ companyIdentifier: 0x0001,
+ },
+ {
+ companyIdentifier: 0x0001,
+ }
+ ]
+}];
+
+bluetooth_test(
+ (t) => promise_rejects_js(
+ t, TypeError, requestDeviceWithTrustedClick({filters})),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.window.js
new file mode 100644
index 0000000000..cd10288ddb
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.window.js
@@ -0,0 +1,18 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A name containing unicode characters whose utf8 length ' +
+ 'is less than 30 must not throw an error.';
+// \u2764's UTF-8 representation is 3 bytes long.
+// 9 chars * 3 bytes/char = 27 bytes
+const valid_unicode_name = '\u2764'.repeat(9);
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: valid_unicode_name},
+ requestDeviceOptions: {filters: [{name: valid_unicode_name}]}
+ });
+ device => assert_equals(device.name, valid_unicode_name)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.window.js
new file mode 100644
index 0000000000..494f324ee2
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.window.js
@@ -0,0 +1,18 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'A namePrefix containing unicode characters whose utf8 ' +
+ 'length is less than 30 must not throw an error.';
+// \u2764's UTF-8 representation is 3 bytes long.
+// 9 chars * 3 bytes/char = 27 bytes
+const valid_unicode_name = '\u2764'.repeat(9);
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: valid_unicode_name},
+ requestDeviceOptions: {filters: [{namePrefix: valid_unicode_name}]}
+ });
+ device => assert_equals(device.name, valid_unicode_name)
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.window.js
new file mode 100644
index 0000000000..bfba220f47
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Invalid optional service must reject the promise.';
+const expected = new TypeError();
+const test_specs = [
+ {optionalServices: ['wrong_service'], filters: [{services: ['heart_rate']}]},
+ {
+ optionalServices: ['wrong_service'],
+ filters: [{services: ['heart_rate'], name: 'Name'}]
+ },
+ {
+ optionalServices: ['wrong_service'],
+ filters: [{services: ['heart_rate'], namePrefix: 'Pre'}]
+ },
+ {
+ optionalServices: ['wrong_service'],
+ filters: [{services: ['heart_rate'], name: 'Name', namePrefix: 'Pre'}]
+ },
+ {optionalServices: ['wrong_service'], filters: [{name: 'Name'}]}, {
+ optionalServices: ['wrong_service'],
+ filters: [{name: 'Name', namePrefix: 'Pre'}]
+ },
+ {optionalServices: ['wrong_service'], filters: [{namePrefix: 'Pre'}]}
+];
+
+bluetooth_test(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {
+ test_promises = test_promises.then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(args), expected));
+ });
+ return test_promises;
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.window.js
new file mode 100644
index 0000000000..352437d0e5
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Invalid service must reject the promise.';
+const expected = new TypeError();
+
+bluetooth_test(() => {
+ let test_promises = Promise.resolve();
+ generateRequestDeviceArgsWithServices(['wrong_service']).forEach(args => {
+ test_promises = test_promises.then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(args), expected));
+ });
+ return test_promises;
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/cross-origin-iframe.sub.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/cross-origin-iframe.sub.https.window.js
new file mode 100644
index 0000000000..d802a86279
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/cross-origin-iframe.sub.https.window.js
@@ -0,0 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request device from a unique origin. ' +
+ 'Should reject with SecurityError.';
+const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
+ '/bluetooth/resources/health-thermometer-iframe.html'
+let iframe = document.createElement('iframe');
+
+bluetooth_test(async (t) => {
+ await setUpHealthThermometerDevice();
+
+ // 1. Load the iframe.
+ const iframeWatcher = new EventWatcher(t, iframe, ['load']);
+ iframe.src = cross_origin_src;
+ document.body.appendChild(iframe);
+ await iframeWatcher.wait_for('load');
+
+ // 2. Request the device from the iframe.
+ const windowWatcher = new EventWatcher(t, window, ['message']);
+ iframe.contentWindow.postMessage({type: 'RequestDevice'}, '*');
+ const messageEvent = await windowWatcher.wait_for('message');
+ assert_equals(
+ messageEvent.data,
+ 'FAIL: SecurityError: Failed to execute \'requestDevice\' on \'Bluetooth\': Access to the feature "bluetooth" is disallowed by permissions policy.');
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/discovery-succeeds.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/discovery-succeeds.https.window.js
new file mode 100644
index 0000000000..4941d185ca
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/discovery-succeeds.https.window.js
@@ -0,0 +1,31 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Discover a device using alias, name, or UUID.';
+
+const test_specs = [
+ {
+ filters: [{services: [health_thermometer.alias]}],
+ },
+ {
+ filters: [{services: [health_thermometer.name]}],
+ },
+ {
+ filters: [{services: [health_thermometer.uuid]}],
+ },
+];
+
+bluetooth_test(
+ () => setUpHealthThermometerDevice().then(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {
+ test_promises = test_promises.then(async () => {
+ const device = await requestDeviceWithTrustedClick(args);
+ assert_equals(device.constructor.name, 'BluetoothDevice');
+ });
+ });
+ return test_promises;
+ }),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/doesnt-consume-user-gesture.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/doesnt-consume-user-gesture.https.window.js
new file mode 100644
index 0000000000..9c742733e1
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/doesnt-consume-user-gesture.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice calls do not consume user gestures.';
+
+bluetooth_test(
+ () => setUpHealthThermometerAndHeartRateDevices().then(
+ () => callWithTrustedClick(() => {
+ let first = navigator.bluetooth.requestDevice(
+ {filters: [{services: ['heart_rate']}]});
+ let second = navigator.bluetooth.requestDevice(
+ {filters: [{services: ['heart_rate']}]});
+ return Promise.all([
+ first.then(
+ device =>
+ assert_equals(device.constructor.name, 'BluetoothDevice')),
+ second.then(
+ device =>
+ assert_equals(device.constructor.name, 'BluetoothDevice')),
+ ]);
+ })),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/filter-matches.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/filter-matches.https.window.js
new file mode 100644
index 0000000000..1a0f52ac30
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/filter-matches.https.window.js
@@ -0,0 +1,76 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Matches a filter if all present members match.';
+let matching_services = [health_thermometer.uuid];
+let matching_name = 'Health Thermometer';
+let matching_namePrefix = 'Health';
+let matching_manufacturerData = [{companyIdentifier: 0x0001}];
+
+let test_specs = [
+ {
+ filters: [{
+ services: matching_services,
+ }]
+ },
+ {
+ filters: [{
+ services: matching_services,
+ name: matching_name,
+ }]
+ },
+ {filters: [{services: matching_services, namePrefix: matching_namePrefix}]}, {
+ filters: [
+ {services: matching_services, manufacturerData: matching_manufacturerData}
+ ]
+ },
+ {
+ filters: [{
+ name: matching_name,
+ }],
+ optionalServices: matching_services
+ },
+ {
+ filters: [{namePrefix: matching_namePrefix}],
+ optionalServices: matching_services
+ },
+ {
+ filters: [{manufacturerData: matching_manufacturerData}],
+ optionalServices: matching_services
+ },
+ {
+ filters: [{
+ name: matching_name,
+ namePrefix: matching_namePrefix,
+ manufacturerData: matching_manufacturerData
+ }],
+ optionalServices: matching_services
+ },
+ {
+ filters: [{
+ services: matching_services,
+ name: matching_name,
+ namePrefix: matching_namePrefix,
+ manufacturerData: matching_manufacturerData
+ }]
+ }
+];
+
+bluetooth_test(
+ () => setUpHealthThermometerDevice().then(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {
+ test_promises =
+ test_promises.then(() => requestDeviceWithTrustedClick(args))
+ .then(device => {
+ // We always have access to the services in matching_services
+ // because we include them in a filter or in optionalServices.
+ assert_equals(device.name, matching_name);
+ assert_true(device.name.startsWith(matching_namePrefix));
+ });
+ });
+ return test_promises;
+ }),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/le-not-supported.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/le-not-supported.https.window.js
new file mode 100644
index 0000000000..c961ab4492
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/le-not-supported.https.window.js
@@ -0,0 +1,15 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Reject with NotFoundError if Bluetooth is not supported.';
+const expected =
+ new DOMException('Bluetooth Low Energy not available.', 'NotFoundError');
+
+bluetooth_test(
+ () => navigator.bluetooth.test.setLESupported(false).then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick({acceptAllDevices: true}), expected,
+ 'Bluetooth Low Energy is not supported.')),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js
new file mode 100644
index 0000000000..c4c0e80532
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js
@@ -0,0 +1,139 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Matches a filter when manufacturer data match.';
+
+let test_specs = [
+ {
+ filters: [{
+ manufacturerData: [{
+ companyIdentifier: 0x0001,
+ }],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [{
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01]),
+ }],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [{
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01]),
+ mask: new Uint8Array([0xff]),
+ }],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [{
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ }],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [{
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ mask: new Uint8Array([0xff, 0x01]),
+ }],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [
+ {
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ mask: new Uint8Array([0xff, 0x01]),
+ },
+ {
+ companyIdentifier: 0x0002,
+ }
+ ],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [
+ {
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ mask: new Uint8Array([0xff, 0x01]),
+ },
+ {
+ companyIdentifier: 0x0002,
+ dataPrefix: new Uint8Array([0x03]),
+ }
+ ],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [
+ {
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ mask: new Uint8Array([0xff, 0x01]),
+ },
+ {
+ companyIdentifier: 0x0002,
+ dataPrefix: new Uint8Array([0x03]),
+ mask: new Uint8Array([0xff]),
+ }
+ ],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [
+ {
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ mask: new Uint8Array([0xff, 0x01]),
+ },
+ {
+ companyIdentifier: 0x0002,
+ dataPrefix: new Uint8Array([0x03, 0x04]),
+ }
+ ],
+ }],
+ },
+ {
+ filters: [{
+ manufacturerData: [
+ {
+ companyIdentifier: 0x0001,
+ dataPrefix: new Uint8Array([0x01, 0x02]),
+ mask: new Uint8Array([0xff, 0x01]),
+ },
+ {
+ companyIdentifier: 0x0002,
+ dataPrefix: new Uint8Array([0x03, 0x04]),
+ mask: new Uint8Array([0xff, 0xff])
+ }
+ ],
+ }],
+ },
+];
+
+bluetooth_test(
+ () => setUpHealthThermometerDevice().then(() => {
+ let test_promises = Promise.resolve();
+ test_specs.forEach(args => {
+ test_promises = test_promises.then(async () => {
+ const device = await requestDeviceWithTrustedClick(args);
+ assert_equals(device.name, 'Health Thermometer');
+ });
+ });
+ return test_promises;
+ }),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.window.js
new file mode 100644
index 0000000000..2ff22cb702
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.window.js
@@ -0,0 +1,14 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'An empty name device can be obtained by empty name filter.'
+
+bluetooth_test(async () => {
+ let {device} = await setUpPreconnectedFakeDevice({
+ fakeDeviceOptions: {name: ''},
+ requestDeviceOptions: {filters: [{name: ''}]}
+ });
+ assert_equals(device.name, '');
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/not-processing-user-gesture.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/not-processing-user-gesture.https.window.js
new file mode 100644
index 0000000000..a063b61163
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/not-processing-user-gesture.https.window.js
@@ -0,0 +1,18 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Requires a user gesture.';
+const expected = new DOMException(
+ 'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+ 'Must be handling a user gesture to show a permission request.',
+ 'SecurityError');
+
+bluetooth_test(
+ () => setUpHealthThermometerAndHeartRateDevices().then(
+ () => assert_promise_rejects_with_message(
+ navigator.bluetooth.requestDevice(
+ {filters: [{services: ['heart_rate']}]}),
+ expected, 'User gesture is required')),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/radio-not-present.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/radio-not-present.https.window.js
new file mode 100644
index 0000000000..b55d63c6ff
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/radio-not-present.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Reject with NotFoundError if there is no BT radio present.';
+const expected =
+ new DOMException('Bluetooth adapter not available.', 'NotFoundError');
+
+bluetooth_test(
+ () => navigator.bluetooth.test.simulateCentral({state: 'absent'})
+ .then(
+ () => assert_promise_rejects_with_message(
+ requestDeviceWithTrustedClick(
+ {filters: [{services: ['generic_access']}]}),
+ expected, 'Bluetooth adapter is not present.')),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html b/testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html
new file mode 100644
index 0000000000..df348dd39e
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html
@@ -0,0 +1,13 @@
+<!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.bluetooth.requestDevice(),
+ 'requestDevice() should throw a SecurityError DOMException when called from a context where the top-level document has an opaque origin.');
+ }, 'Calls to Bluetooth APIs from an origin with opaque top origin get blocked.');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html.headers b/testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html.headers
new file mode 100644
index 0000000000..c7e4e7cc5b
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/reject_opaque_origin.https.html.headers
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/request-from-iframe.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/request-from-iframe.https.window.js
new file mode 100644
index 0000000000..d3f3cf897f
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/request-from-iframe.https.window.js
@@ -0,0 +1,43 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Concurrent requestDevice calls in iframes work.';
+const iframes = [];
+for (let i = 0; i < 5; i++) {
+ iframes.push(document.createElement('iframe'));
+}
+
+bluetooth_test(
+ () => setUpHealthThermometerAndHeartRateDevices()
+ // 1. Load the iframes.
+ .then(() => {
+ let promises = [];
+ for (let iframe of iframes) {
+ iframe.src =
+ '/bluetooth/resources/health-thermometer-iframe.html';
+ document.body.appendChild(iframe);
+ promises.push(new Promise(
+ resolve => iframe.addEventListener('load', resolve)));
+ }
+ return Promise.all(promises);
+ })
+ // 2. Request the device from the iframes.
+ .then(() => new Promise(async (resolve) => {
+ let numMessages = 0;
+ window.onmessage =
+ messageEvent => {
+ assert_equals(messageEvent.data, 'Success');
+ if (++numMessages === iframes.length) {
+ resolve();
+ }
+ }
+
+ for (let iframe of iframes) {
+ await callWithTrustedClick(
+ () => iframe.contentWindow.postMessage(
+ {type: 'RequestDevice'}, '*'));
+ }
+ })),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.js
new file mode 100644
index 0000000000..2101cf0d6b
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.js
@@ -0,0 +1,35 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Request device from a unique origin. ' +
+ 'Should reject with SecurityError.';
+const expected =
+ 'FAIL: SecurityError: Failed to execute \'requestDevice\' on ' +
+ '\'Bluetooth\': Access to the feature "bluetooth" is disallowed by ' +
+ 'permissions policy.';
+
+let iframe = document.createElement('iframe');
+
+bluetooth_test(
+ () => getConnectedHealthThermometerDevice()
+ // 1. Load the iframe.
+ .then(() => new Promise(resolve => {
+ iframe.sandbox.add('allow-scripts');
+ iframe.src =
+ '/bluetooth/resources/health-thermometer-iframe.html';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('load', resolve);
+ }))
+ // 2. Request the device from the iframe.
+ .then(() => new Promise(resolve => {
+ iframe.contentWindow.postMessage(
+ {type: 'RequestDevice'}, '*');
+
+ window.onmessage = messageEvent => {
+ assert_equals(messageEvent.data, expected);
+ resolve();
+ }
+ })),
+ test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/same-device.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/same-device.https.window.js
new file mode 100644
index 0000000000..41a42cf4c8
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/same-device.https.window.js
@@ -0,0 +1,19 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Returned device should always be the same.';
+let devices = [];
+
+bluetooth_test(async () => {
+ await setUpHealthThermometerAndHeartRateDevices();
+ devices.push(await requestDeviceWithTrustedClick(
+ {filters: [{services: [heart_rate.alias]}]}));
+ devices.push(await requestDeviceWithTrustedClick(
+ {filters: [{services: [heart_rate.name]}]}));
+ devices.push(await requestDeviceWithTrustedClick(
+ {filters: [{services: [heart_rate.uuid]}]}));
+ assert_equals(devices[0], devices[1]);
+ assert_equals(devices[1], devices[2]);
+}, test_desc);
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/sandboxed_iframe.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/sandboxed_iframe.https.window.js
new file mode 100644
index 0000000000..e9192a9305
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/sandboxed_iframe.https.window.js
@@ -0,0 +1,27 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+
+'use strict';
+
+let iframe = document.createElement('iframe');
+
+bluetooth_test(async () => {
+ await getConnectedHealthThermometerDevice();
+ await new Promise(resolve => {
+ iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
+ iframe.sandbox.add('allow-scripts');
+ iframe.allow = 'bluetooth';
+ document.body.appendChild(iframe);
+ iframe.addEventListener('load', resolve);
+ });
+ await new Promise(resolve => {
+ iframe.contentWindow.postMessage({type: 'RequestDevice'}, '*');
+
+ window.addEventListener('message', (messageEvent) => {
+ assert_false(/^FAIL: .*/.test(messageEvent.data));
+ resolve();
+ });
+ });
+}, 'Calls to Bluetooth APIs from a sandboxed iframe are valid.'); \ No newline at end of file
diff --git a/testing/web-platform/tests/bluetooth/requestDevice/single-filter-single-service.https.window.js b/testing/web-platform/tests/bluetooth/requestDevice/single-filter-single-service.https.window.js
new file mode 100644
index 0000000000..67afad0b93
--- /dev/null
+++ b/testing/web-platform/tests/bluetooth/requestDevice/single-filter-single-service.https.window.js
@@ -0,0 +1,14 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Simple filter selects matching device.';
+
+bluetooth_test(
+ () => setUpHealthThermometerAndHeartRateDevices()
+ .then(
+ () => requestDeviceWithTrustedClick(
+ {filters: [{services: ['health_thermometer']}]}))
+ .then(device => assert_equals(device.name, 'Health Thermometer')),
+ test_desc);