diff --git a/src/bidiMapper/CommandProcessor.ts b/src/bidiMapper/CommandProcessor.ts index 229e63e086..4a3439552d 100644 --- a/src/bidiMapper/CommandProcessor.ts +++ b/src/bidiMapper/CommandProcessor.ts @@ -31,6 +31,7 @@ import type {Result} from '../utils/result.js'; import {BidiNoOpParser} from './BidiNoOpParser.js'; import type {BidiCommandParameterParser} from './BidiParser.js'; +import {BluetoohProcessor} from './domains/bluetooth/BluetoothProcessor'; import {BrowserProcessor} from './domains/browser/BrowserProcessor.js'; import {CdpProcessor} from './domains/cdp/CdpProcessor.js'; import {BrowsingContextProcessor} from './domains/context/BrowsingContextProcessor.js'; @@ -60,6 +61,7 @@ type CommandProcessorEventsMap = { export class CommandProcessor extends EventEmitter { // keep-sorted start + #bluetoothProcessor: BluetoohProcessor; #browserProcessor: BrowserProcessor; #browsingContextProcessor: BrowsingContextProcessor; #cdpProcessor: CdpProcessor; @@ -98,6 +100,10 @@ export class CommandProcessor extends EventEmitter { const preloadScriptStorage = new PreloadScriptStorage(); // keep-sorted start block=yes + this.#bluetoothProcessor = new BluetoohProcessor( + eventManager, + browsingContextStorage + ); this.#browserProcessor = new BrowserProcessor(browserCdpClient); this.#browsingContextProcessor = new BrowsingContextProcessor( cdpConnection, @@ -106,6 +112,7 @@ export class CommandProcessor extends EventEmitter { eventManager, browsingContextStorage, realmStorage, + this.#bluetoothProcessor, networkStorage, preloadScriptStorage, acceptInsecureCerts, diff --git a/src/bidiMapper/domains/bluetooth/BluetoothProcessor.ts b/src/bidiMapper/domains/bluetooth/BluetoothProcessor.ts new file mode 100644 index 0000000000..c2a68a79a2 --- /dev/null +++ b/src/bidiMapper/domains/bluetooth/BluetoothProcessor.ts @@ -0,0 +1,50 @@ +/** + * Copyright 2024 Google LLC. + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type {BrowsingContextStorage} from '../context/BrowsingContextStorage.js'; +import type {CdpTarget} from '../context/CdpTarget.js'; +import type {EventManager} from '../session/EventManager.js'; + +export class BluetoohProcessor { + #eventManager: EventManager; + #browsingContextStorage: BrowsingContextStorage; + + constructor( + eventManager: EventManager, + browsingContextStorage: BrowsingContextStorage + ) { + this.#eventManager = eventManager; + this.#browsingContextStorage = browsingContextStorage; + } + + onCdpTargetCreated(cdpTarget: CdpTarget) { + cdpTarget.cdpClient.on('DeviceAccess.deviceRequestPrompted', (event) => { + this.#eventManager.registerEvent( + { + type: 'event', + method: 'bluetooth.requestDevicePromptOpened', + params: { + context: cdpTarget.id, + prompt: event.id, + devices: event.devices, + }, + }, + cdpTarget.id + ); + }); + } +} diff --git a/src/bidiMapper/domains/context/BrowsingContextProcessor.ts b/src/bidiMapper/domains/context/BrowsingContextProcessor.ts index 3739c8340a..db8c23e0c1 100644 --- a/src/bidiMapper/domains/context/BrowsingContextProcessor.ts +++ b/src/bidiMapper/domains/context/BrowsingContextProcessor.ts @@ -28,6 +28,7 @@ import { } from '../../../protocol/protocol.js'; import {CdpErrorConstants} from '../../../utils/CdpErrorConstants.js'; import {LogType, type LoggerFn} from '../../../utils/log.js'; +import type {BluetoohProcessor} from '../bluetooth/BluetoothProcessor.js'; import type {NetworkStorage} from '../network/NetworkStorage.js'; import type {PreloadScriptStorage} from '../script/PreloadScriptStorage.js'; import type {Realm} from '../script/Realm.js'; @@ -51,6 +52,8 @@ export class BrowsingContextProcessor { readonly #selfTargetId: string; readonly #eventManager: EventManager; + readonly #bluetoothProcessor: BluetoohProcessor; + readonly #browsingContextStorage: BrowsingContextStorage; readonly #networkStorage: NetworkStorage; readonly #acceptInsecureCerts: boolean; @@ -67,6 +70,7 @@ export class BrowsingContextProcessor { eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, + bluetoothProcessor: BluetoohProcessor, networkStorage: NetworkStorage, preloadScriptStorage: PreloadScriptStorage, acceptInsecureCerts: boolean, @@ -74,6 +78,7 @@ export class BrowsingContextProcessor { logger?: LoggerFn ) { this.#acceptInsecureCerts = acceptInsecureCerts; + this.#bluetoothProcessor = bluetoothProcessor; this.#cdpConnection = cdpConnection; this.#browserCdpClient = browserCdpClient; this.#selfTargetId = selfTargetId; @@ -499,6 +504,7 @@ export class BrowsingContextProcessor { ); this.#networkStorage.onCdpTargetCreated(target); + this.#bluetoothProcessor.onCdpTargetCreated(target); return target; } diff --git a/src/bidiMapper/domains/context/CdpTarget.ts b/src/bidiMapper/domains/context/CdpTarget.ts index 7e3460f7ef..5ec385c768 100644 --- a/src/bidiMapper/domains/context/CdpTarget.ts +++ b/src/bidiMapper/domains/context/CdpTarget.ts @@ -152,6 +152,7 @@ export class CdpTarget { }), this.#initAndEvaluatePreloadScripts(), this.#cdpClient.sendCommand('Runtime.runIfWaitingForDebugger'), + this.#initDeviceAccessIfNeeded(), ]); } catch (error: any) { // The target might have been closed before the initialization finished. @@ -226,6 +227,14 @@ export class CdpTarget { } } + async #initDeviceAccessIfNeeded(): Promise { + const enabled = this.isSubscribedTo(BiDiModule.Bluetooth); + await this.#cdpClient.sendCommand( + enabled ? 'DeviceAccess.enable' : 'DeviceAccess.disable' + ); + return; + } + #setEventListeners() { this.#cdpClient.on('*', (event, params) => { // We may encounter uses for EventEmitter other than CDP events, diff --git a/src/bidiMapper/domains/session/SubscriptionManager.ts b/src/bidiMapper/domains/session/SubscriptionManager.ts index 1960cc8790..189625fafd 100644 --- a/src/bidiMapper/domains/session/SubscriptionManager.ts +++ b/src/bidiMapper/domains/session/SubscriptionManager.ts @@ -234,6 +234,11 @@ export class SubscriptionManager { this.subscribe(specificEvent, contextId, channel) ); return; + case ChromiumBidi.BiDiModule.Bluetooth: + Object.values(ChromiumBidi.Bluetooth.EventNames).map((specificEvent) => + this.subscribe(specificEvent, contextId, channel) + ); + return; default: // Intentionally left empty. } diff --git a/src/protocol/chromium-bidi.ts b/src/protocol/chromium-bidi.ts index a3b61d7a93..e4e13086ec 100644 --- a/src/protocol/chromium-bidi.ts +++ b/src/protocol/chromium-bidi.ts @@ -16,6 +16,7 @@ */ import type * as Cdp from './cdp.js'; +import type * as WebDriverBidiBluetooth from './generated/webdriver-bidi-bluetooth.js'; import type * as WebDriverBidiPermissions from './generated/webdriver-bidi-permissions.js'; import type * as WebDriverBidi from './generated/webdriver-bidi.js'; @@ -23,6 +24,7 @@ export type EventNames = // keep-sorted start | Cdp.EventNames | `${BiDiModule}` + | `${Bluetooth.EventNames}` | `${BrowsingContext.EventNames}` | `${Log.EventNames}` | `${Network.EventNames}` @@ -31,6 +33,7 @@ export type EventNames = export enum BiDiModule { // keep-sorted start + Bluetooth = 'bluetooth', Browser = 'browser', BrowsingContext = 'browsingContext', Cdp = 'cdp', @@ -88,6 +91,13 @@ export namespace Network { } } +export namespace Bluetooth { + export enum EventNames { + RequestDevicePromptOpened = 'bluetooth.requestDevicePromptOpened', + RequestDevicePromptClosed = 'bluetooth.requestDevicePromptClosed', + } +} + export type Command = ( | WebDriverBidi.Command | Cdp.Command @@ -104,7 +114,14 @@ export type CommandResponse = | WebDriverBidi.CommandResponse | Cdp.CommandResponse; -export type Event = WebDriverBidi.Event | Cdp.Event; +export type BluetoothEvent = { + type: 'event'; +} & ( + | WebDriverBidiBluetooth.Bluetooth.RequestDevicePromptOpened + | (WebDriverBidiBluetooth.Bluetooth.RequestDevicePromptClosed & + WebDriverBidi.Extensible) +); +export type Event = WebDriverBidi.Event | Cdp.Event | BluetoothEvent; export const EVENT_NAMES = new Set([ // keep-sorted start @@ -120,6 +137,6 @@ export type ResultData = WebDriverBidi.ResultData | Cdp.ResultData; export type BidiPlusChannel = string | null; -export type Message = (WebDriverBidi.Message | Cdp.Message) & { +export type Message = (WebDriverBidi.Message | Cdp.Message | BluetoothEvent) & { channel?: BidiPlusChannel; }; diff --git a/src/protocol/protocol.ts b/src/protocol/protocol.ts index 6977ccf340..d2aa5ba815 100644 --- a/src/protocol/protocol.ts +++ b/src/protocol/protocol.ts @@ -19,3 +19,4 @@ export * as ChromiumBidi from './chromium-bidi.js'; export * from './generated/webdriver-bidi.js'; export * from './ErrorResponse.js'; export * from './generated/webdriver-bidi-permissions.js'; +export * from './generated/webdriver-bidi-bluetooth.js';