web-bluetooth-test.js (24488B)
1 'use strict'; 2 3 const content = {}; 4 const bluetooth = {}; 5 const MOJO_CHOOSER_EVENT_TYPE_MAP = {}; 6 7 function toMojoCentralState(state) { 8 switch (state) { 9 case 'absent': 10 return bluetooth.mojom.CentralState.ABSENT; 11 case 'powered-off': 12 return bluetooth.mojom.CentralState.POWERED_OFF; 13 case 'powered-on': 14 return bluetooth.mojom.CentralState.POWERED_ON; 15 default: 16 throw `Unsupported value ${state} for state.`; 17 } 18 } 19 20 // Converts bluetooth.mojom.WriteType to a string. If |writeType| is 21 // invalid, this method will throw. 22 function writeTypeToString(writeType) { 23 switch (writeType) { 24 case bluetooth.mojom.WriteType.kNone: 25 return 'none'; 26 case bluetooth.mojom.WriteType.kWriteDefaultDeprecated: 27 return 'default-deprecated'; 28 case bluetooth.mojom.WriteType.kWriteWithResponse: 29 return 'with-response'; 30 case bluetooth.mojom.WriteType.kWriteWithoutResponse: 31 return 'without-response'; 32 default: 33 throw `Unknown bluetooth.mojom.WriteType: ${writeType}`; 34 } 35 } 36 37 // Canonicalizes UUIDs and converts them to Mojo UUIDs. 38 function canonicalizeAndConvertToMojoUUID(uuids) { 39 let canonicalUUIDs = uuids.map(val => ({uuid: BluetoothUUID.getService(val)})); 40 return canonicalUUIDs; 41 } 42 43 // Converts WebIDL a record<DOMString, BufferSource> to a map<K, array<uint8>> to 44 // use for Mojo, where the value for K is calculated using keyFn. 45 function convertToMojoMap(record, keyFn, isNumberKey = false) { 46 let map = new Map(); 47 for (const [key, value] of Object.entries(record)) { 48 let buffer = ArrayBuffer.isView(value) ? value.buffer : value; 49 if (isNumberKey) { 50 let numberKey = parseInt(key); 51 if (Number.isNaN(numberKey)) 52 throw `Map key ${key} is not a number`; 53 map.set(keyFn(numberKey), Array.from(new Uint8Array(buffer))); 54 continue; 55 } 56 map.set(keyFn(key), Array.from(new Uint8Array(buffer))); 57 } 58 return map; 59 } 60 61 function ArrayToMojoCharacteristicProperties(arr) { 62 const struct = {}; 63 arr.forEach(property => { struct[property] = true; }); 64 return struct; 65 } 66 67 class FakeBluetooth { 68 constructor() { 69 this.fake_bluetooth_ptr_ = new bluetooth.mojom.FakeBluetoothRemote(); 70 this.fake_bluetooth_ptr_.$.bindNewPipeAndPassReceiver().bindInBrowser('process'); 71 this.fake_central_ = null; 72 } 73 74 // Set it to indicate whether the platform supports BLE. For example, 75 // Windows 7 is a platform that doesn't support Low Energy. On the other 76 // hand Windows 10 is a platform that does support LE, even if there is no 77 // Bluetooth radio present. 78 async setLESupported(supported) { 79 if (typeof supported !== 'boolean') throw 'Type Not Supported'; 80 await this.fake_bluetooth_ptr_.setLESupported(supported); 81 } 82 83 // Returns a promise that resolves with a FakeCentral that clients can use 84 // to simulate events that a device in the Central/Observer role would 85 // receive as well as monitor the operations performed by the device in the 86 // Central/Observer role. 87 // Calls sets LE as supported. 88 // 89 // A "Central" object would allow its clients to receive advertising events 90 // and initiate connections to peripherals i.e. operations of two roles 91 // defined by the Bluetooth Spec: Observer and Central. 92 // See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an 93 // LE Physical Transport". 94 async simulateCentral({state}) { 95 if (this.fake_central_) 96 throw 'simulateCentral() should only be called once'; 97 98 await this.setLESupported(true); 99 100 let {fakeCentral: fake_central_ptr} = 101 await this.fake_bluetooth_ptr_.simulateCentral( 102 toMojoCentralState(state)); 103 this.fake_central_ = new FakeCentral(fake_central_ptr); 104 return this.fake_central_; 105 } 106 107 // Returns true if there are no pending responses. 108 async allResponsesConsumed() { 109 let {consumed} = await this.fake_bluetooth_ptr_.allResponsesConsumed(); 110 return consumed; 111 } 112 113 // Returns a promise that resolves with a FakeChooser that clients can use to 114 // simulate chooser events. 115 async getManualChooser() { 116 if (typeof this.fake_chooser_ === 'undefined') { 117 this.fake_chooser_ = new FakeChooser(); 118 } 119 return this.fake_chooser_; 120 } 121 } 122 123 // FakeCentral allows clients to simulate events that a device in the 124 // Central/Observer role would receive as well as monitor the operations 125 // performed by the device in the Central/Observer role. 126 class FakeCentral { 127 constructor(fake_central_ptr) { 128 this.fake_central_ptr_ = fake_central_ptr; 129 this.peripherals_ = new Map(); 130 } 131 132 // Simulates a peripheral with |address|, |name|, |manufacturerData| and 133 // |known_service_uuids| that has already been connected to the system. If the 134 // peripheral existed already it updates its name, manufacturer data, and 135 // known UUIDs. |known_service_uuids| should be an array of 136 // BluetoothServiceUUIDs 137 // https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid 138 // 139 // Platforms offer methods to retrieve devices that have already been 140 // connected to the system or weren't connected through the UA e.g. a user 141 // connected a peripheral through the system's settings. This method is 142 // intended to simulate peripherals that those methods would return. 143 async simulatePreconnectedPeripheral( 144 {address, name, manufacturerData = {}, knownServiceUUIDs = []}) { 145 await this.fake_central_ptr_.simulatePreconnectedPeripheral( 146 address, name, 147 convertToMojoMap(manufacturerData, Number, true /* isNumberKey */), 148 canonicalizeAndConvertToMojoUUID(knownServiceUUIDs)); 149 150 return this.fetchOrCreatePeripheral_(address); 151 } 152 153 // Simulates an advertisement packet described by |scanResult| being received 154 // from a device. If central is currently scanning, the device will appear on 155 // the list of discovered devices. 156 async simulateAdvertisementReceived(scanResult) { 157 // Create a deep-copy to prevent the original |scanResult| from being 158 // modified when the UUIDs, manufacturer, and service data are converted. 159 let clonedScanResult = JSON.parse(JSON.stringify(scanResult)); 160 161 if ('uuids' in scanResult.scanRecord) { 162 clonedScanResult.scanRecord.uuids = 163 canonicalizeAndConvertToMojoUUID(scanResult.scanRecord.uuids); 164 } 165 166 // Convert the optional appearance and txPower fields to the corresponding 167 // Mojo structures, since Mojo does not support optional interger values. If 168 // the fields are undefined, set the hasValue field as false and value as 0. 169 // Otherwise, set the hasValue field as true and value with the field value. 170 const has_appearance = 'appearance' in scanResult.scanRecord; 171 clonedScanResult.scanRecord.appearance = { 172 hasValue: has_appearance, 173 value: (has_appearance ? scanResult.scanRecord.appearance : 0) 174 } 175 176 const has_tx_power = 'txPower' in scanResult.scanRecord; 177 clonedScanResult.scanRecord.txPower = { 178 hasValue: has_tx_power, 179 value: (has_tx_power ? scanResult.scanRecord.txPower : 0) 180 } 181 182 // Convert manufacturerData from a record<DOMString, BufferSource> into a 183 // map<uint8, array<uint8>> for Mojo. 184 if ('manufacturerData' in scanResult.scanRecord) { 185 clonedScanResult.scanRecord.manufacturerData = convertToMojoMap( 186 scanResult.scanRecord.manufacturerData, Number, 187 true /* isNumberKey */); 188 } 189 190 // Convert serviceData from a record<DOMString, BufferSource> into a 191 // map<string, array<uint8>> for Mojo. 192 if ('serviceData' in scanResult.scanRecord) { 193 clonedScanResult.scanRecord.serviceData.serviceData = convertToMojoMap( 194 scanResult.scanRecord.serviceData, BluetoothUUID.getService, 195 false /* isNumberKey */); 196 } 197 198 await this.fake_central_ptr_.simulateAdvertisementReceived( 199 clonedScanResult); 200 201 return this.fetchOrCreatePeripheral_(clonedScanResult.deviceAddress); 202 } 203 204 // Simulates a change in the central device described by |state|. For example, 205 // setState('powered-off') can be used to simulate the central device powering 206 // off. 207 // 208 // This method should be used for any central state changes after 209 // simulateCentral() has been called to create a FakeCentral object. 210 async setState(state) { 211 await this.fake_central_ptr_.setState(toMojoCentralState(state)); 212 } 213 214 // Create a fake_peripheral object from the given address. 215 fetchOrCreatePeripheral_(address) { 216 let peripheral = this.peripherals_.get(address); 217 if (peripheral === undefined) { 218 peripheral = new FakePeripheral(address, this.fake_central_ptr_); 219 this.peripherals_.set(address, peripheral); 220 } 221 return peripheral; 222 } 223 } 224 225 class FakePeripheral { 226 constructor(address, fake_central_ptr) { 227 this.address = address; 228 this.fake_central_ptr_ = fake_central_ptr; 229 } 230 231 // Adds a fake GATT Service with |uuid| to be discovered when discovering 232 // the peripheral's GATT Attributes. Returns a FakeRemoteGATTService 233 // corresponding to this service. |uuid| should be a BluetoothServiceUUIDs 234 // https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid 235 async addFakeService({uuid}) { 236 let {serviceId: service_id} = await this.fake_central_ptr_.addFakeService( 237 this.address, {uuid: BluetoothUUID.getService(uuid)}); 238 239 if (service_id === null) throw 'addFakeService failed'; 240 241 return new FakeRemoteGATTService( 242 service_id, this.address, this.fake_central_ptr_); 243 } 244 245 // Sets the next GATT Connection request response to |code|. |code| could be 246 // an HCI Error Code from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a 247 // number outside that range returned by specific platforms e.g. Android 248 // returns 0x101 to signal a GATT failure 249 // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE 250 async setNextGATTConnectionResponse({code}) { 251 let {success} = 252 await this.fake_central_ptr_.setNextGATTConnectionResponse( 253 this.address, code); 254 255 if (success !== true) throw 'setNextGATTConnectionResponse failed.'; 256 } 257 258 // Sets the next GATT Discovery request response for peripheral with 259 // |address| to |code|. |code| could be an HCI Error Code from 260 // BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a number outside that 261 // range returned by specific platforms e.g. Android returns 0x101 to signal 262 // a GATT failure 263 // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE 264 // 265 // The following procedures defined at BT 4.2 Vol 3 Part G Section 4. 266 // "GATT Feature Requirements" are used to discover attributes of the 267 // GATT Server: 268 // - Primary Service Discovery 269 // - Relationship Discovery 270 // - Characteristic Discovery 271 // - Characteristic Descriptor Discovery 272 // This method aims to simulate the response once all of these procedures 273 // have completed or if there was an error during any of them. 274 async setNextGATTDiscoveryResponse({code}) { 275 let {success} = 276 await this.fake_central_ptr_.setNextGATTDiscoveryResponse( 277 this.address, code); 278 279 if (success !== true) throw 'setNextGATTDiscoveryResponse failed.'; 280 } 281 282 // Simulates a GATT disconnection from the peripheral with |address|. 283 async simulateGATTDisconnection() { 284 let {success} = 285 await this.fake_central_ptr_.simulateGATTDisconnection(this.address); 286 287 if (success !== true) throw 'simulateGATTDisconnection failed.'; 288 } 289 290 // Simulates an Indication from the peripheral's GATT `Service Changed` 291 // Characteristic from BT 4.2 Vol 3 Part G 7.1. This Indication is signaled 292 // when services, characteristics, or descriptors are changed, added, or 293 // removed. 294 // 295 // The value for `Service Changed` is a range of attribute handles that have 296 // changed. However, this testing specification works at an abstracted 297 // level and does not expose setting attribute handles when adding 298 // attributes. Consequently, this simulate method should include the full 299 // range of all the peripheral's attribute handle values. 300 async simulateGATTServicesChanged() { 301 let {success} = 302 await this.fake_central_ptr_.simulateGATTServicesChanged(this.address); 303 304 if (success !== true) throw 'simulateGATTServicesChanged failed.'; 305 } 306 } 307 308 class FakeRemoteGATTService { 309 constructor(service_id, peripheral_address, fake_central_ptr) { 310 this.service_id_ = service_id; 311 this.peripheral_address_ = peripheral_address; 312 this.fake_central_ptr_ = fake_central_ptr; 313 } 314 315 // Adds a fake GATT Characteristic with |uuid| and |properties| 316 // to this fake service. The characteristic will be found when discovering 317 // the peripheral's GATT Attributes. Returns a FakeRemoteGATTCharacteristic 318 // corresponding to the added characteristic. 319 async addFakeCharacteristic({uuid, properties}) { 320 let {characteristicId: characteristic_id} = 321 await this.fake_central_ptr_.addFakeCharacteristic( 322 {uuid: BluetoothUUID.getCharacteristic(uuid)}, 323 ArrayToMojoCharacteristicProperties(properties), 324 this.service_id_, 325 this.peripheral_address_); 326 327 if (characteristic_id === null) throw 'addFakeCharacteristic failed'; 328 329 return new FakeRemoteGATTCharacteristic( 330 characteristic_id, this.service_id_, 331 this.peripheral_address_, this.fake_central_ptr_); 332 } 333 334 // Removes the fake GATT service from its fake peripheral. 335 async remove() { 336 let {success} = 337 await this.fake_central_ptr_.removeFakeService( 338 this.service_id_, 339 this.peripheral_address_); 340 341 if (!success) throw 'remove failed'; 342 } 343 } 344 345 class FakeRemoteGATTCharacteristic { 346 constructor(characteristic_id, service_id, peripheral_address, 347 fake_central_ptr) { 348 this.ids_ = [characteristic_id, service_id, peripheral_address]; 349 this.descriptors_ = []; 350 this.fake_central_ptr_ = fake_central_ptr; 351 } 352 353 // Adds a fake GATT Descriptor with |uuid| to be discovered when 354 // discovering the peripheral's GATT Attributes. Returns a 355 // FakeRemoteGATTDescriptor corresponding to this descriptor. |uuid| should 356 // be a BluetoothDescriptorUUID 357 // https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothdescriptoruuid 358 async addFakeDescriptor({uuid}) { 359 let {descriptorId: descriptor_id} = 360 await this.fake_central_ptr_.addFakeDescriptor( 361 {uuid: BluetoothUUID.getDescriptor(uuid)}, ...this.ids_); 362 363 if (descriptor_id === null) throw 'addFakeDescriptor failed'; 364 365 let fake_descriptor = new FakeRemoteGATTDescriptor( 366 descriptor_id, ...this.ids_, this.fake_central_ptr_); 367 this.descriptors_.push(fake_descriptor); 368 369 return fake_descriptor; 370 } 371 372 // Sets the next read response for characteristic to |code| and |value|. 373 // |code| could be a GATT Error Response from 374 // BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range 375 // returned by specific platforms e.g. Android returns 0x101 to signal a GATT 376 // failure. 377 // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE 378 async setNextReadResponse(gatt_code, value=null) { 379 if (gatt_code === 0 && value === null) { 380 throw '|value| can\'t be null if read should success.'; 381 } 382 if (gatt_code !== 0 && value !== null) { 383 throw '|value| must be null if read should fail.'; 384 } 385 386 let {success} = 387 await this.fake_central_ptr_.setNextReadCharacteristicResponse( 388 gatt_code, value, ...this.ids_); 389 390 if (!success) throw 'setNextReadCharacteristicResponse failed'; 391 } 392 393 // Sets the next write response for this characteristic to |code|. If 394 // writing to a characteristic that only supports 'write_without_response' 395 // the set response will be ignored. 396 // |code| could be a GATT Error Response from 397 // BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range 398 // returned by specific platforms e.g. Android returns 0x101 to signal a GATT 399 // failure. 400 async setNextWriteResponse(gatt_code) { 401 let {success} = 402 await this.fake_central_ptr_.setNextWriteCharacteristicResponse( 403 gatt_code, ...this.ids_); 404 405 if (!success) throw 'setNextWriteCharacteristicResponse failed'; 406 } 407 408 // Sets the next subscribe to notifications response for characteristic with 409 // |characteristic_id| in |service_id| and in |peripheral_address| to 410 // |code|. |code| could be a GATT Error Response from BT 4.2 Vol 3 Part F 411 // 3.4.1.1 Error Response or a number outside that range returned by 412 // specific platforms e.g. Android returns 0x101 to signal a GATT failure. 413 async setNextSubscribeToNotificationsResponse(gatt_code) { 414 let {success} = 415 await this.fake_central_ptr_.setNextSubscribeToNotificationsResponse( 416 gatt_code, ...this.ids_); 417 418 if (!success) throw 'setNextSubscribeToNotificationsResponse failed'; 419 } 420 421 // Sets the next unsubscribe to notifications response for characteristic with 422 // |characteristic_id| in |service_id| and in |peripheral_address| to 423 // |code|. |code| could be a GATT Error Response from BT 4.2 Vol 3 Part F 424 // 3.4.1.1 Error Response or a number outside that range returned by 425 // specific platforms e.g. Android returns 0x101 to signal a GATT failure. 426 async setNextUnsubscribeFromNotificationsResponse(gatt_code) { 427 let {success} = 428 await this.fake_central_ptr_.setNextUnsubscribeFromNotificationsResponse( 429 gatt_code, ...this.ids_); 430 431 if (!success) throw 'setNextUnsubscribeToNotificationsResponse failed'; 432 } 433 434 // Returns true if notifications from the characteristic have been subscribed 435 // to. 436 async isNotifying() { 437 let {success, isNotifying} = 438 await this.fake_central_ptr_.isNotifying(...this.ids_); 439 440 if (!success) throw 'isNotifying failed'; 441 442 return isNotifying; 443 } 444 445 // Gets the last successfully written value to the characteristic and its 446 // write type. Write type is one of 'none', 'default-deprecated', 447 // 'with-response', 'without-response'. Returns {lastValue: null, 448 // lastWriteType: 'none'} if no value has yet been written to the 449 // characteristic. 450 async getLastWrittenValue() { 451 let {success, value, writeType} = 452 await this.fake_central_ptr_.getLastWrittenCharacteristicValue( 453 ...this.ids_); 454 455 if (!success) throw 'getLastWrittenCharacteristicValue failed'; 456 457 return {lastValue: value, lastWriteType: writeTypeToString(writeType)}; 458 } 459 460 // Removes the fake GATT Characteristic from its fake service. 461 async remove() { 462 let {success} = 463 await this.fake_central_ptr_.removeFakeCharacteristic(...this.ids_); 464 465 if (!success) throw 'remove failed'; 466 } 467 } 468 469 class FakeRemoteGATTDescriptor { 470 constructor(descriptor_id, 471 characteristic_id, 472 service_id, 473 peripheral_address, 474 fake_central_ptr) { 475 this.ids_ = [ 476 descriptor_id, characteristic_id, service_id, peripheral_address]; 477 this.fake_central_ptr_ = fake_central_ptr; 478 } 479 480 // Sets the next read response for descriptor to |code| and |value|. 481 // |code| could be a GATT Error Response from 482 // BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range 483 // returned by specific platforms e.g. Android returns 0x101 to signal a GATT 484 // failure. 485 // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE 486 async setNextReadResponse(gatt_code, value=null) { 487 if (gatt_code === 0 && value === null) { 488 throw '|value| cannot be null if read should succeed.'; 489 } 490 if (gatt_code !== 0 && value !== null) { 491 throw '|value| must be null if read should fail.'; 492 } 493 494 let {success} = 495 await this.fake_central_ptr_.setNextReadDescriptorResponse( 496 gatt_code, value, ...this.ids_); 497 498 if (!success) throw 'setNextReadDescriptorResponse failed'; 499 } 500 501 // Sets the next write response for this descriptor to |code|. 502 // |code| could be a GATT Error Response from 503 // BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range 504 // returned by specific platforms e.g. Android returns 0x101 to signal a GATT 505 // failure. 506 async setNextWriteResponse(gatt_code) { 507 let {success} = 508 await this.fake_central_ptr_.setNextWriteDescriptorResponse( 509 gatt_code, ...this.ids_); 510 511 if (!success) throw 'setNextWriteDescriptorResponse failed'; 512 } 513 514 // Gets the last successfully written value to the descriptor. 515 // Returns null if no value has yet been written to the descriptor. 516 async getLastWrittenValue() { 517 let {success, value} = 518 await this.fake_central_ptr_.getLastWrittenDescriptorValue( 519 ...this.ids_); 520 521 if (!success) throw 'getLastWrittenDescriptorValue failed'; 522 523 return value; 524 } 525 526 // Removes the fake GATT Descriptor from its fake characteristic. 527 async remove() { 528 let {success} = 529 await this.fake_central_ptr_.removeFakeDescriptor(...this.ids_); 530 531 if (!success) throw 'remove failed'; 532 } 533 } 534 535 // FakeChooser allows clients to simulate user actions on a Bluetooth chooser, 536 // and records the events produced by the Bluetooth chooser. 537 class FakeChooser { 538 constructor() { 539 let fakeBluetoothChooserFactoryRemote = 540 new content.mojom.FakeBluetoothChooserFactoryRemote(); 541 fakeBluetoothChooserFactoryRemote.$.bindNewPipeAndPassReceiver().bindInBrowser('process'); 542 543 this.fake_bluetooth_chooser_ptr_ = 544 new content.mojom.FakeBluetoothChooserRemote(); 545 this.fake_bluetooth_chooser_client_receiver_ = 546 new content.mojom.FakeBluetoothChooserClientReceiver(this); 547 fakeBluetoothChooserFactoryRemote.createFakeBluetoothChooser( 548 this.fake_bluetooth_chooser_ptr_.$.bindNewPipeAndPassReceiver(), 549 this.fake_bluetooth_chooser_client_receiver_.$.associateAndPassRemote()); 550 551 this.events_ = new Array(); 552 this.event_listener_ = null; 553 } 554 555 // If the chooser has received more events than |numOfEvents| this function 556 // will reject the promise, else it will wait until |numOfEvents| events are 557 // received before resolving with an array of |FakeBluetoothChooserEvent| 558 // objects. 559 async waitForEvents(numOfEvents) { 560 return new Promise(resolve => { 561 if (this.events_.length > numOfEvents) { 562 throw `Asked for ${numOfEvents} event(s), but received ` + 563 `${this.events_.length}.`; 564 } 565 566 this.event_listener_ = () => { 567 if (this.events_.length === numOfEvents) { 568 let result = Array.from(this.events_); 569 this.event_listener_ = null; 570 this.events_ = []; 571 resolve(result); 572 } 573 }; 574 this.event_listener_(); 575 }); 576 } 577 578 async selectPeripheral(peripheral) { 579 if (!(peripheral instanceof FakePeripheral)) { 580 throw '|peripheral| must be an instance of FakePeripheral'; 581 } 582 await this.fake_bluetooth_chooser_ptr_.selectPeripheral(peripheral.address); 583 } 584 585 async cancel() { 586 await this.fake_bluetooth_chooser_ptr_.cancel(); 587 } 588 589 async rescan() { 590 await this.fake_bluetooth_chooser_ptr_.rescan(); 591 } 592 593 onEvent(chooserEvent) { 594 chooserEvent.type = MOJO_CHOOSER_EVENT_TYPE_MAP[chooserEvent.type]; 595 this.events_.push(chooserEvent); 596 if (this.event_listener_ !== null) { 597 this.event_listener_(); 598 } 599 } 600 } 601 602 async function initializeChromiumResources() { 603 content.mojom = await import( 604 '/gen/content/web_test/common/fake_bluetooth_chooser.mojom.m.js'); 605 bluetooth.mojom = await import( 606 '/gen/device/bluetooth/public/mojom/emulation/fake_bluetooth.mojom.m.js'); 607 608 const map = MOJO_CHOOSER_EVENT_TYPE_MAP; 609 const types = content.mojom.ChooserEventType; 610 map[types.CHOOSER_OPENED] = 'chooser-opened'; 611 map[types.CHOOSER_CLOSED] = 'chooser-closed'; 612 map[types.ADAPTER_REMOVED] = 'adapter-removed'; 613 map[types.ADAPTER_DISABLED] = 'adapter-disabled'; 614 map[types.ADAPTER_ENABLED] = 'adapter-enabled'; 615 map[types.DISCOVERY_FAILED_TO_START] = 'discovery-failed-to-start'; 616 map[types.DISCOVERING] = 'discovering'; 617 map[types.DISCOVERY_IDLE] = 'discovery-idle'; 618 map[types.ADD_OR_UPDATE_DEVICE] = 'add-or-update-device'; 619 620 // If this line fails, it means that current environment does not support the 621 // Web Bluetooth Test API. 622 try { 623 navigator.bluetooth.test = new FakeBluetooth(); 624 } catch { 625 throw 'Web Bluetooth Test API is not implemented on this ' + 626 'environment. See the bluetooth README at ' + 627 'https://github.com/web-platform-tests/wpt/blob/master/bluetooth/README.md#web-bluetooth-testing'; 628 } 629 }