Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,079 changes: 560 additions & 519 deletions Pipfile.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/bidiMapper/BidiNoOpParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

import type {
Autofill,
Browser,
BrowsingContext,
Cdp,
Expand All @@ -33,6 +34,13 @@ import type {
import type {BidiCommandParameterParser} from './BidiParser.js';

export class BidiNoOpParser implements BidiCommandParameterParser {
// Autofill module
// keep-sorted start block=yes
parseAutofillTriggerParams(params: unknown): Autofill.TriggerParameters {
return params as Autofill.TriggerParameters;
}
// keep-sorted end

// Bluetooth module
// keep-sorted start block=yes
parseDisableSimulationParameters(
Expand Down
6 changes: 6 additions & 0 deletions src/bidiMapper/BidiParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

import type {
Autofill,
Bluetooth,
Browser,
BrowsingContext,
Expand All @@ -31,6 +32,11 @@ import type {
} from '../protocol/protocol.js';

export interface BidiCommandParameterParser {
// Autofill module
// keep-sorted start block=yes
parseAutofillTriggerParams(params: unknown): Autofill.TriggerParameters;
// keep-sorted end

// Bluetooth module
// keep-sorted start block=yes
parseDisableSimulationParameters(
Expand Down
12 changes: 12 additions & 0 deletions src/bidiMapper/CommandProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type {Result} from '../utils/result.js';
import {BidiNoOpParser} from './BidiNoOpParser.js';
import type {BidiCommandParameterParser} from './BidiParser.js';
import type {MapperOptions} from './MapperOptions.js';
import {AutofillProcessor} from './modules/autofill/AutofillProcessor.js';
import type {BluetoothProcessor} from './modules/bluetooth/BluetoothProcessor.js';
import {BrowserProcessor} from './modules/browser/BrowserProcessor.js';
import type {ContextConfigStorage} from './modules/browser/ContextConfigStorage.js';
Expand Down Expand Up @@ -66,6 +67,7 @@ interface CommandProcessorEventsMap extends Record<string | symbol, unknown> {

export class CommandProcessor extends EventEmitter<CommandProcessorEventsMap> {
// keep-sorted start
#autofillProcessor: AutofillProcessor;
#bluetoothProcessor: BluetoothProcessor;
#browserCdpClient: CdpClient;
#browserProcessor: BrowserProcessor;
Expand Down Expand Up @@ -105,6 +107,7 @@ export class CommandProcessor extends EventEmitter<CommandProcessorEventsMap> {
this.#logger = logger;

this.#bluetoothProcessor = bluetoothProcessor;
this.#autofillProcessor = new AutofillProcessor(browsingContextStorage);

// keep-sorted start block=yes
this.#browserProcessor = new BrowserProcessor(
Expand Down Expand Up @@ -165,8 +168,17 @@ export class CommandProcessor extends EventEmitter<CommandProcessorEventsMap> {
command: ChromiumBidi.Command,
): Promise<ChromiumBidi.ResultData> {
switch (command.method) {
// Autofill module
// keep-sorted start block=yes
case 'autofill.trigger':
return await this.#autofillProcessor.trigger(
this.#parser.parseAutofillTriggerParams(command.params),
);
// keep-sorted end

// Bluetooth module
// keep-sorted start block=yes

case 'bluetooth.disableSimulation':
return await this.#bluetoothProcessor.disableSimulation(
this.#parser.parseDisableSimulationParameters(command.params),
Expand Down
104 changes: 104 additions & 0 deletions src/bidiMapper/modules/autofill/AutofillProcessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Copyright 2025 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 {CdpClient} from '../../../cdp/CdpClient.js';
import {
type Autofill,
type EmptyResult,
NoSuchNodeException,
UnsupportedOperationException,
} from '../../../protocol/protocol.js';
import type {BrowsingContextStorage} from '../context/BrowsingContextStorage.js';
import {parseSharedId} from '../script/SharedId.js';

/**
* Responsible for handling the `autofill` module.
*/
export class AutofillProcessor {
readonly #browsingContextStorage: BrowsingContextStorage;

constructor(browsingContextStorage: BrowsingContextStorage) {
this.#browsingContextStorage = browsingContextStorage;
}

/**
* Triggers autofill for a specific element with the provided field data.
*
* @param params Parameters for the autofill.trigger command
* @returns An empty result
*/
async trigger(params: Autofill.TriggerParameters): Promise<EmptyResult> {
try {
// Get the browsing context from the parameters
const context = this.#browsingContextStorage.getContext(params.context);

// Parse the shared ID to get frame, document, and backend node ID
const parsedSharedId = parseSharedId(params.element.sharedId);
if (parsedSharedId === null) {
throw new NoSuchNodeException(
`SharedId "${params.element.sharedId}" was not found.`,
);
}

const {frameId, documentId, backendNodeId} = parsedSharedId;

// Assert that the frame matches the current context (if frameId is available)
if (frameId !== undefined && frameId !== params.context) {
throw new NoSuchNodeException(
`SharedId "${params.element.sharedId}" belongs to different frame. Current frame is ${params.context}.`,
);
}

// Assert that the document matches the current context's navigable ID
if (context.navigableId !== documentId) {
throw new NoSuchNodeException(
`SharedId "${params.element.sharedId}" belongs to different document. Current document is ${context.navigableId}.`,
);
}

// Cast to `any` as a temporary workaround for prototyping, since the TypeScript types
// for CDP in "Chromium BiDi" aren't automatically updated with local changes.

// Based on the Autofill.pdl definition, call the correct CDP method
// The PDL shows: command trigger with fieldId as DOM.BackendNodeId

// First, we need to enable the Autofill domain
try {
await context.cdpTarget.cdpClient.sendCommand('Autofill.enable');
} catch (enableErr) {
console.log('Failed to enable Autofill domain:', (enableErr as Error).message);
}

// Call the trigger method with the correct parameters from PDL
await (context.cdpTarget.cdpClient as any).sendCommand('Autofill.trigger', {
fieldId: backendNodeId, // DOM.BackendNodeId from parsed shared ID
frameId: frameId, // Page.FrameId from parsed shared ID
card: params.card, // optional CreditCard
address: params.address, // optional Address
});

return {};
} catch (err) {
if ((err as Error).message.includes('command was not found')) {
throw new UnsupportedOperationException(
'Autofill.trigger() is not supported by this browser',
);
}
throw err;
}
}
}
8 changes: 8 additions & 0 deletions src/bidiTab/BidiParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
import type {BidiCommandParameterParser} from '../bidiMapper/BidiMapper.js';
import type {
Autofill,
Bluetooth,
Browser,
BrowsingContext,
Expand All @@ -32,6 +33,13 @@ import type {
import * as Parser from '../protocol-parser/protocol-parser.js';

export class BidiParser implements BidiCommandParameterParser {
// Autofill module
// keep-sorted start block=yes
parseAutofillTriggerParams(params: unknown): Autofill.TriggerParameters {
return Parser.Autofill.parseTriggerParameters(params);
}
// keep-sorted end

// Bluetooth module
// keep-sorted start block=yes
parseDisableSimulationParameters(
Expand Down
Loading