From ef4b4043c3473bda27f8127fe7c8e1c51cabb6e8 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 13 Jan 2025 16:07:26 -0800 Subject: [PATCH] chore(bidi): use original key name when computing bidi value Use orignal key name from the API, to not lose information due to double mapping. We previously switched to using code for pw -> bidi mapping(https://github.com/microsoft/playwright/pull/34246), but it does not work for `H` and `h`: ``` ControlLeft => code:ControlLeft key:Control ControlRight => code:ControlRight key:Control H => code:KeyH key:H h => code:KeyH key:h ``` --- .../playwright-core/src/server/bidi/bidiInput.ts | 9 ++++----- .../src/server/bidi/third_party/bidiKeyboard.ts | 14 +++++++------- .../playwright-core/src/server/chromium/crInput.ts | 10 ++++++---- .../playwright-core/src/server/firefox/ffInput.ts | 11 +++++++---- packages/playwright-core/src/server/input.ts | 11 +++++------ .../playwright-core/src/server/webkit/wkInput.ts | 12 +++++++----- 6 files changed, 36 insertions(+), 31 deletions(-) diff --git a/packages/playwright-core/src/server/bidi/bidiInput.ts b/packages/playwright-core/src/server/bidi/bidiInput.ts index 01b773178d..e149bb4a3f 100644 --- a/packages/playwright-core/src/server/bidi/bidiInput.ts +++ b/packages/playwright-core/src/server/bidi/bidiInput.ts @@ -31,16 +31,15 @@ export class RawKeyboardImpl implements input.RawKeyboard { this._session = session; } - async keydown(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise { + async keydown(modifiers: Set, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise { const actions: bidi.Input.KeySourceAction[] = []; - actions.push({ type: 'keyDown', value: getBidiKeyValue(code) }); - // TODO: add modifiers? + actions.push({ type: 'keyDown', value: getBidiKeyValue(keyName) }); await this._performActions(actions); } - async keyup(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise { + async keyup(modifiers: Set, keyName: string, description: input.KeyDescription): Promise { const actions: bidi.Input.KeySourceAction[] = []; - actions.push({ type: 'keyUp', value: getBidiKeyValue(code) }); + actions.push({ type: 'keyUp', value: getBidiKeyValue(keyName) }); await this._performActions(actions); } diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts b/packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts index c2d60ff66b..611135e2c8 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiKeyboard.ts @@ -7,18 +7,18 @@ /* eslint-disable curly */ -export const getBidiKeyValue = (code: string) => { - switch (code) { +export const getBidiKeyValue = (keyName: string) => { + switch (keyName) { case '\r': case '\n': - code = 'Enter'; + keyName = 'Enter'; break; } // Measures the number of code points rather than UTF-16 code units. - if ([...code].length === 1) { - return code; + if ([...keyName].length === 1) { + return keyName; } - switch (code) { + switch (keyName) { case 'Cancel': return '\uE001'; case 'Help': @@ -228,6 +228,6 @@ export const getBidiKeyValue = (code: string) => { case 'Quote': return '"'; default: - throw new Error(`Unknown key: "${code}"`); + throw new Error(`Unknown key: "${keyName}"`); } }; diff --git a/packages/playwright-core/src/server/chromium/crInput.ts b/packages/playwright-core/src/server/chromium/crInput.ts index bbfd973d10..e08f3b0233 100644 --- a/packages/playwright-core/src/server/chromium/crInput.ts +++ b/packages/playwright-core/src/server/chromium/crInput.ts @@ -50,14 +50,15 @@ export class RawKeyboardImpl implements input.RawKeyboard { return commands.map(c => c.substring(0, c.length - 1)); } - async keydown(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise { + async keydown(modifiers: Set, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise { + const { code, key, location, text } = description; if (code === 'Escape' && await this._dragManger.cancelDrag()) return; const commands = this._commandsForCode(code, modifiers); await this._client.send('Input.dispatchKeyEvent', { type: text ? 'keyDown' : 'rawKeyDown', modifiers: toModifiersMask(modifiers), - windowsVirtualKeyCode: keyCodeWithoutLocation, + windowsVirtualKeyCode: description.keyCodeWithoutLocation, code, commands, key, @@ -69,12 +70,13 @@ export class RawKeyboardImpl implements input.RawKeyboard { }); } - async keyup(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise { + async keyup(modifiers: Set, keyName: string, description: input.KeyDescription): Promise { + const { code, key, location } = description; await this._client.send('Input.dispatchKeyEvent', { type: 'keyUp', modifiers: toModifiersMask(modifiers), key, - windowsVirtualKeyCode: keyCodeWithoutLocation, + windowsVirtualKeyCode: description.keyCodeWithoutLocation, code, location }); diff --git a/packages/playwright-core/src/server/firefox/ffInput.ts b/packages/playwright-core/src/server/firefox/ffInput.ts index 66f35399a5..42d2c71ebf 100644 --- a/packages/playwright-core/src/server/firefox/ffInput.ts +++ b/packages/playwright-core/src/server/firefox/ffInput.ts @@ -61,13 +61,15 @@ export class RawKeyboardImpl implements input.RawKeyboard { this._client = client; } - async keydown(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise { + async keydown(modifiers: Set, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise { + let text = description.text; // Firefox will figure out Enter by itself if (text === '\r') text = ''; + const { code, key, location } = description; await this._client.send('Page.dispatchKeyEvent', { type: 'keydown', - keyCode: keyCodeWithoutLocation, + keyCode: description.keyCodeWithoutLocation, code, key, repeat: autoRepeat, @@ -76,11 +78,12 @@ export class RawKeyboardImpl implements input.RawKeyboard { }); } - async keyup(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise { + async keyup(modifiers: Set, keyName: string, description: input.KeyDescription): Promise { + const { code, key, location } = description; await this._client.send('Page.dispatchKeyEvent', { type: 'keyup', key, - keyCode: keyCodeWithoutLocation, + keyCode: description.keyCodeWithoutLocation, code, location, repeat: false diff --git a/packages/playwright-core/src/server/input.ts b/packages/playwright-core/src/server/input.ts index 4e4c95a8f3..f8beb789f8 100644 --- a/packages/playwright-core/src/server/input.ts +++ b/packages/playwright-core/src/server/input.ts @@ -22,7 +22,7 @@ import type { CallMetadata } from './instrumentation'; export const keypadLocation = keyboardLayout.keypadLocation; -type KeyDescription = { +export type KeyDescription = { keyCode: number, keyCodeWithoutLocation: number, key: string, @@ -35,8 +35,8 @@ type KeyDescription = { const kModifiers: types.KeyboardModifier[] = ['Alt', 'Control', 'Meta', 'Shift']; export interface RawKeyboard { - keydown(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise; - keyup(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise; + keydown(modifiers: Set, keyName: string, description: KeyDescription, autoRepeat: boolean): Promise; + keyup(modifiers: Set, keyName: string, description: KeyDescription): Promise; sendText(text: string): Promise; } @@ -55,8 +55,7 @@ export class Keyboard { this._pressedKeys.add(description.code); if (kModifiers.includes(description.key as types.KeyboardModifier)) this._pressedModifiers.add(description.key as types.KeyboardModifier); - const text = description.text; - await this._raw.keydown(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location, autoRepeat, text); + await this._raw.keydown(this._pressedModifiers, key, description, autoRepeat); } private _keyDescriptionForString(str: string): KeyDescription { @@ -77,7 +76,7 @@ export class Keyboard { if (kModifiers.includes(description.key as types.KeyboardModifier)) this._pressedModifiers.delete(description.key as types.KeyboardModifier); this._pressedKeys.delete(description.code); - await this._raw.keyup(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location); + await this._raw.keyup(this._pressedModifiers, key, description); } async insertText(text: string) { diff --git a/packages/playwright-core/src/server/webkit/wkInput.ts b/packages/playwright-core/src/server/webkit/wkInput.ts index 0732f246d1..5769fd6465 100644 --- a/packages/playwright-core/src/server/webkit/wkInput.ts +++ b/packages/playwright-core/src/server/webkit/wkInput.ts @@ -59,12 +59,13 @@ export class RawKeyboardImpl implements input.RawKeyboard { this._session = session; } - async keydown(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise { + async keydown(modifiers: Set, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise { const parts = []; for (const modifier of (['Shift', 'Control', 'Alt', 'Meta']) as types.KeyboardModifier[]) { if (modifiers.has(modifier)) parts.push(modifier); } + const { code, keyCode, key, text } = description; parts.push(code); const shortcut = parts.join('+'); let commands = macEditingCommands[shortcut]; @@ -80,18 +81,19 @@ export class RawKeyboardImpl implements input.RawKeyboard { unmodifiedText: text, autoRepeat, macCommands: commands, - isKeypad: location === input.keypadLocation + isKeypad: description.location === input.keypadLocation }); } - async keyup(modifiers: Set, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise { + async keyup(modifiers: Set, keyName: string, description: input.KeyDescription): Promise { + const { code, key } = description; await this._pageProxySession.send('Input.dispatchKeyEvent', { type: 'keyUp', modifiers: toModifiersMask(modifiers), key, - windowsVirtualKeyCode: keyCode, + windowsVirtualKeyCode: description.keyCode, code, - isKeypad: location === input.keypadLocation + isKeypad: description.location === input.keypadLocation }); }