summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/puppeteer-core/src/bidi/Input.ts
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/puppeteer-core/src/bidi/Input.ts')
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/bidi/Input.ts732
1 files changed, 732 insertions, 0 deletions
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/bidi/Input.ts b/remote/test/puppeteer/packages/puppeteer-core/src/bidi/Input.ts
new file mode 100644
index 0000000000..5406556d64
--- /dev/null
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/bidi/Input.ts
@@ -0,0 +1,732 @@
+/**
+ * @license
+ * Copyright 2017 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
+
+import type {Point} from '../api/ElementHandle.js';
+import {
+ Keyboard,
+ Mouse,
+ MouseButton,
+ Touchscreen,
+ type KeyDownOptions,
+ type KeyPressOptions,
+ type KeyboardTypeOptions,
+ type MouseClickOptions,
+ type MouseMoveOptions,
+ type MouseOptions,
+ type MouseWheelOptions,
+} from '../api/Input.js';
+import {UnsupportedOperation} from '../common/Errors.js';
+import type {KeyInput} from '../common/USKeyboardLayout.js';
+
+import type {BrowsingContext} from './BrowsingContext.js';
+import type {BidiPage} from './Page.js';
+
+const enum InputId {
+ Mouse = '__puppeteer_mouse',
+ Keyboard = '__puppeteer_keyboard',
+ Wheel = '__puppeteer_wheel',
+ Finger = '__puppeteer_finger',
+}
+
+enum SourceActionsType {
+ None = 'none',
+ Key = 'key',
+ Pointer = 'pointer',
+ Wheel = 'wheel',
+}
+
+enum ActionType {
+ Pause = 'pause',
+ KeyDown = 'keyDown',
+ KeyUp = 'keyUp',
+ PointerUp = 'pointerUp',
+ PointerDown = 'pointerDown',
+ PointerMove = 'pointerMove',
+ Scroll = 'scroll',
+}
+
+const getBidiKeyValue = (key: KeyInput) => {
+ switch (key) {
+ case '\r':
+ case '\n':
+ key = 'Enter';
+ break;
+ }
+ // Measures the number of code points rather than UTF-16 code units.
+ if ([...key].length === 1) {
+ return key;
+ }
+ switch (key) {
+ case 'Cancel':
+ return '\uE001';
+ case 'Help':
+ return '\uE002';
+ case 'Backspace':
+ return '\uE003';
+ case 'Tab':
+ return '\uE004';
+ case 'Clear':
+ return '\uE005';
+ case 'Enter':
+ return '\uE007';
+ case 'Shift':
+ case 'ShiftLeft':
+ return '\uE008';
+ case 'Control':
+ case 'ControlLeft':
+ return '\uE009';
+ case 'Alt':
+ case 'AltLeft':
+ return '\uE00A';
+ case 'Pause':
+ return '\uE00B';
+ case 'Escape':
+ return '\uE00C';
+ case 'PageUp':
+ return '\uE00E';
+ case 'PageDown':
+ return '\uE00F';
+ case 'End':
+ return '\uE010';
+ case 'Home':
+ return '\uE011';
+ case 'ArrowLeft':
+ return '\uE012';
+ case 'ArrowUp':
+ return '\uE013';
+ case 'ArrowRight':
+ return '\uE014';
+ case 'ArrowDown':
+ return '\uE015';
+ case 'Insert':
+ return '\uE016';
+ case 'Delete':
+ return '\uE017';
+ case 'NumpadEqual':
+ return '\uE019';
+ case 'Numpad0':
+ return '\uE01A';
+ case 'Numpad1':
+ return '\uE01B';
+ case 'Numpad2':
+ return '\uE01C';
+ case 'Numpad3':
+ return '\uE01D';
+ case 'Numpad4':
+ return '\uE01E';
+ case 'Numpad5':
+ return '\uE01F';
+ case 'Numpad6':
+ return '\uE020';
+ case 'Numpad7':
+ return '\uE021';
+ case 'Numpad8':
+ return '\uE022';
+ case 'Numpad9':
+ return '\uE023';
+ case 'NumpadMultiply':
+ return '\uE024';
+ case 'NumpadAdd':
+ return '\uE025';
+ case 'NumpadSubtract':
+ return '\uE027';
+ case 'NumpadDecimal':
+ return '\uE028';
+ case 'NumpadDivide':
+ return '\uE029';
+ case 'F1':
+ return '\uE031';
+ case 'F2':
+ return '\uE032';
+ case 'F3':
+ return '\uE033';
+ case 'F4':
+ return '\uE034';
+ case 'F5':
+ return '\uE035';
+ case 'F6':
+ return '\uE036';
+ case 'F7':
+ return '\uE037';
+ case 'F8':
+ return '\uE038';
+ case 'F9':
+ return '\uE039';
+ case 'F10':
+ return '\uE03A';
+ case 'F11':
+ return '\uE03B';
+ case 'F12':
+ return '\uE03C';
+ case 'Meta':
+ case 'MetaLeft':
+ return '\uE03D';
+ case 'ShiftRight':
+ return '\uE050';
+ case 'ControlRight':
+ return '\uE051';
+ case 'AltRight':
+ return '\uE052';
+ case 'MetaRight':
+ return '\uE053';
+ case 'Digit0':
+ return '0';
+ case 'Digit1':
+ return '1';
+ case 'Digit2':
+ return '2';
+ case 'Digit3':
+ return '3';
+ case 'Digit4':
+ return '4';
+ case 'Digit5':
+ return '5';
+ case 'Digit6':
+ return '6';
+ case 'Digit7':
+ return '7';
+ case 'Digit8':
+ return '8';
+ case 'Digit9':
+ return '9';
+ case 'KeyA':
+ return 'a';
+ case 'KeyB':
+ return 'b';
+ case 'KeyC':
+ return 'c';
+ case 'KeyD':
+ return 'd';
+ case 'KeyE':
+ return 'e';
+ case 'KeyF':
+ return 'f';
+ case 'KeyG':
+ return 'g';
+ case 'KeyH':
+ return 'h';
+ case 'KeyI':
+ return 'i';
+ case 'KeyJ':
+ return 'j';
+ case 'KeyK':
+ return 'k';
+ case 'KeyL':
+ return 'l';
+ case 'KeyM':
+ return 'm';
+ case 'KeyN':
+ return 'n';
+ case 'KeyO':
+ return 'o';
+ case 'KeyP':
+ return 'p';
+ case 'KeyQ':
+ return 'q';
+ case 'KeyR':
+ return 'r';
+ case 'KeyS':
+ return 's';
+ case 'KeyT':
+ return 't';
+ case 'KeyU':
+ return 'u';
+ case 'KeyV':
+ return 'v';
+ case 'KeyW':
+ return 'w';
+ case 'KeyX':
+ return 'x';
+ case 'KeyY':
+ return 'y';
+ case 'KeyZ':
+ return 'z';
+ case 'Semicolon':
+ return ';';
+ case 'Equal':
+ return '=';
+ case 'Comma':
+ return ',';
+ case 'Minus':
+ return '-';
+ case 'Period':
+ return '.';
+ case 'Slash':
+ return '/';
+ case 'Backquote':
+ return '`';
+ case 'BracketLeft':
+ return '[';
+ case 'Backslash':
+ return '\\';
+ case 'BracketRight':
+ return ']';
+ case 'Quote':
+ return '"';
+ default:
+ throw new Error(`Unknown key: "${key}"`);
+ }
+};
+
+/**
+ * @internal
+ */
+export class BidiKeyboard extends Keyboard {
+ #page: BidiPage;
+
+ constructor(page: BidiPage) {
+ super();
+ this.#page = page;
+ }
+
+ override async down(
+ key: KeyInput,
+ _options?: Readonly<KeyDownOptions>
+ ): Promise<void> {
+ await this.#page.connection.send('input.performActions', {
+ context: this.#page.mainFrame()._id,
+ actions: [
+ {
+ type: SourceActionsType.Key,
+ id: InputId.Keyboard,
+ actions: [
+ {
+ type: ActionType.KeyDown,
+ value: getBidiKeyValue(key),
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override async up(key: KeyInput): Promise<void> {
+ await this.#page.connection.send('input.performActions', {
+ context: this.#page.mainFrame()._id,
+ actions: [
+ {
+ type: SourceActionsType.Key,
+ id: InputId.Keyboard,
+ actions: [
+ {
+ type: ActionType.KeyUp,
+ value: getBidiKeyValue(key),
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override async press(
+ key: KeyInput,
+ options: Readonly<KeyPressOptions> = {}
+ ): Promise<void> {
+ const {delay = 0} = options;
+ const actions: Bidi.Input.KeySourceAction[] = [
+ {
+ type: ActionType.KeyDown,
+ value: getBidiKeyValue(key),
+ },
+ ];
+ if (delay > 0) {
+ actions.push({
+ type: ActionType.Pause,
+ duration: delay,
+ });
+ }
+ actions.push({
+ type: ActionType.KeyUp,
+ value: getBidiKeyValue(key),
+ });
+ await this.#page.connection.send('input.performActions', {
+ context: this.#page.mainFrame()._id,
+ actions: [
+ {
+ type: SourceActionsType.Key,
+ id: InputId.Keyboard,
+ actions,
+ },
+ ],
+ });
+ }
+
+ override async type(
+ text: string,
+ options: Readonly<KeyboardTypeOptions> = {}
+ ): Promise<void> {
+ const {delay = 0} = options;
+ // This spread separates the characters into code points rather than UTF-16
+ // code units.
+ const values = ([...text] as KeyInput[]).map(getBidiKeyValue);
+ const actions: Bidi.Input.KeySourceAction[] = [];
+ if (delay <= 0) {
+ for (const value of values) {
+ actions.push(
+ {
+ type: ActionType.KeyDown,
+ value,
+ },
+ {
+ type: ActionType.KeyUp,
+ value,
+ }
+ );
+ }
+ } else {
+ for (const value of values) {
+ actions.push(
+ {
+ type: ActionType.KeyDown,
+ value,
+ },
+ {
+ type: ActionType.Pause,
+ duration: delay,
+ },
+ {
+ type: ActionType.KeyUp,
+ value,
+ }
+ );
+ }
+ }
+ await this.#page.connection.send('input.performActions', {
+ context: this.#page.mainFrame()._id,
+ actions: [
+ {
+ type: SourceActionsType.Key,
+ id: InputId.Keyboard,
+ actions,
+ },
+ ],
+ });
+ }
+
+ override async sendCharacter(char: string): Promise<void> {
+ // Measures the number of code points rather than UTF-16 code units.
+ if ([...char].length > 1) {
+ throw new Error('Cannot send more than 1 character.');
+ }
+ const frame = await this.#page.focusedFrame();
+ await frame.isolatedRealm().evaluate(async char => {
+ document.execCommand('insertText', false, char);
+ }, char);
+ }
+}
+
+/**
+ * @internal
+ */
+export interface BidiMouseClickOptions extends MouseClickOptions {
+ origin?: Bidi.Input.Origin;
+}
+
+/**
+ * @internal
+ */
+export interface BidiMouseMoveOptions extends MouseMoveOptions {
+ origin?: Bidi.Input.Origin;
+}
+
+/**
+ * @internal
+ */
+export interface BidiTouchMoveOptions {
+ origin?: Bidi.Input.Origin;
+}
+
+const getBidiButton = (button: MouseButton) => {
+ switch (button) {
+ case MouseButton.Left:
+ return 0;
+ case MouseButton.Middle:
+ return 1;
+ case MouseButton.Right:
+ return 2;
+ case MouseButton.Back:
+ return 3;
+ case MouseButton.Forward:
+ return 4;
+ }
+};
+
+/**
+ * @internal
+ */
+export class BidiMouse extends Mouse {
+ #context: BrowsingContext;
+ #lastMovePoint: Point = {x: 0, y: 0};
+
+ constructor(context: BrowsingContext) {
+ super();
+ this.#context = context;
+ }
+
+ override async reset(): Promise<void> {
+ this.#lastMovePoint = {x: 0, y: 0};
+ await this.#context.connection.send('input.releaseActions', {
+ context: this.#context.id,
+ });
+ }
+
+ override async move(
+ x: number,
+ y: number,
+ options: Readonly<BidiMouseMoveOptions> = {}
+ ): Promise<void> {
+ const from = this.#lastMovePoint;
+ const to = {
+ x: Math.round(x),
+ y: Math.round(y),
+ };
+ const actions: Bidi.Input.PointerSourceAction[] = [];
+ const steps = options.steps ?? 0;
+ for (let i = 0; i < steps; ++i) {
+ actions.push({
+ type: ActionType.PointerMove,
+ x: from.x + (to.x - from.x) * (i / steps),
+ y: from.y + (to.y - from.y) * (i / steps),
+ origin: options.origin,
+ });
+ }
+ actions.push({
+ type: ActionType.PointerMove,
+ ...to,
+ origin: options.origin,
+ });
+ // https://w3c.github.io/webdriver-bidi/#command-input-performActions:~:text=input.PointerMoveAction%20%3D%20%7B%0A%20%20type%3A%20%22pointerMove%22%2C%0A%20%20x%3A%20js%2Dint%2C
+ this.#lastMovePoint = to;
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Mouse,
+ actions,
+ },
+ ],
+ });
+ }
+
+ override async down(options: Readonly<MouseOptions> = {}): Promise<void> {
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Mouse,
+ actions: [
+ {
+ type: ActionType.PointerDown,
+ button: getBidiButton(options.button ?? MouseButton.Left),
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override async up(options: Readonly<MouseOptions> = {}): Promise<void> {
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Mouse,
+ actions: [
+ {
+ type: ActionType.PointerUp,
+ button: getBidiButton(options.button ?? MouseButton.Left),
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override async click(
+ x: number,
+ y: number,
+ options: Readonly<BidiMouseClickOptions> = {}
+ ): Promise<void> {
+ const actions: Bidi.Input.PointerSourceAction[] = [
+ {
+ type: ActionType.PointerMove,
+ x: Math.round(x),
+ y: Math.round(y),
+ origin: options.origin,
+ },
+ ];
+ const pointerDownAction = {
+ type: ActionType.PointerDown,
+ button: getBidiButton(options.button ?? MouseButton.Left),
+ } as const;
+ const pointerUpAction = {
+ type: ActionType.PointerUp,
+ button: pointerDownAction.button,
+ } as const;
+ for (let i = 1; i < (options.count ?? 1); ++i) {
+ actions.push(pointerDownAction, pointerUpAction);
+ }
+ actions.push(pointerDownAction);
+ if (options.delay) {
+ actions.push({
+ type: ActionType.Pause,
+ duration: options.delay,
+ });
+ }
+ actions.push(pointerUpAction);
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Mouse,
+ actions,
+ },
+ ],
+ });
+ }
+
+ override async wheel(
+ options: Readonly<MouseWheelOptions> = {}
+ ): Promise<void> {
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Wheel,
+ id: InputId.Wheel,
+ actions: [
+ {
+ type: ActionType.Scroll,
+ ...(this.#lastMovePoint ?? {
+ x: 0,
+ y: 0,
+ }),
+ deltaX: options.deltaX ?? 0,
+ deltaY: options.deltaY ?? 0,
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override drag(): never {
+ throw new UnsupportedOperation();
+ }
+
+ override dragOver(): never {
+ throw new UnsupportedOperation();
+ }
+
+ override dragEnter(): never {
+ throw new UnsupportedOperation();
+ }
+
+ override drop(): never {
+ throw new UnsupportedOperation();
+ }
+
+ override dragAndDrop(): never {
+ throw new UnsupportedOperation();
+ }
+}
+
+/**
+ * @internal
+ */
+export class BidiTouchscreen extends Touchscreen {
+ #context: BrowsingContext;
+
+ constructor(context: BrowsingContext) {
+ super();
+ this.#context = context;
+ }
+
+ override async touchStart(
+ x: number,
+ y: number,
+ options: BidiTouchMoveOptions = {}
+ ): Promise<void> {
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Finger,
+ parameters: {
+ pointerType: Bidi.Input.PointerType.Touch,
+ },
+ actions: [
+ {
+ type: ActionType.PointerMove,
+ x: Math.round(x),
+ y: Math.round(y),
+ origin: options.origin,
+ },
+ {
+ type: ActionType.PointerDown,
+ button: 0,
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override async touchMove(
+ x: number,
+ y: number,
+ options: BidiTouchMoveOptions = {}
+ ): Promise<void> {
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Finger,
+ parameters: {
+ pointerType: Bidi.Input.PointerType.Touch,
+ },
+ actions: [
+ {
+ type: ActionType.PointerMove,
+ x: Math.round(x),
+ y: Math.round(y),
+ origin: options.origin,
+ },
+ ],
+ },
+ ],
+ });
+ }
+
+ override async touchEnd(): Promise<void> {
+ await this.#context.connection.send('input.performActions', {
+ context: this.#context.id,
+ actions: [
+ {
+ type: SourceActionsType.Pointer,
+ id: InputId.Finger,
+ parameters: {
+ pointerType: Bidi.Input.PointerType.Touch,
+ },
+ actions: [
+ {
+ type: ActionType.PointerUp,
+ button: 0,
+ },
+ ],
+ },
+ ],
+ });
+ }
+}