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
```
This commit is contained in:
Yury Semikhatsky 2025-01-13 16:07:26 -08:00
parent 454b6f938d
commit ef4b4043c3
6 changed files with 36 additions and 31 deletions

View file

@ -31,16 +31,15 @@ export class RawKeyboardImpl implements input.RawKeyboard {
this._session = session;
}
async keydown(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void> {
async keydown(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise<void> {
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<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise<void> {
async keyup(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription): Promise<void> {
const actions: bidi.Input.KeySourceAction[] = [];
actions.push({ type: 'keyUp', value: getBidiKeyValue(code) });
actions.push({ type: 'keyUp', value: getBidiKeyValue(keyName) });
await this._performActions(actions);
}

View file

@ -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}"`);
}
};

View file

@ -50,14 +50,15 @@ export class RawKeyboardImpl implements input.RawKeyboard {
return commands.map(c => c.substring(0, c.length - 1));
}
async keydown(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void> {
async keydown(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise<void> {
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<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise<void> {
async keyup(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription): Promise<void> {
const { code, key, location } = description;
await this._client.send('Input.dispatchKeyEvent', {
type: 'keyUp',
modifiers: toModifiersMask(modifiers),
key,
windowsVirtualKeyCode: keyCodeWithoutLocation,
windowsVirtualKeyCode: description.keyCodeWithoutLocation,
code,
location
});

View file

@ -61,13 +61,15 @@ export class RawKeyboardImpl implements input.RawKeyboard {
this._client = client;
}
async keydown(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void> {
async keydown(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise<void> {
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<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise<void> {
async keyup(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription): Promise<void> {
const { code, key, location } = description;
await this._client.send('Page.dispatchKeyEvent', {
type: 'keyup',
key,
keyCode: keyCodeWithoutLocation,
keyCode: description.keyCodeWithoutLocation,
code,
location,
repeat: false

View file

@ -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<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void>;
keyup(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise<void>;
keydown(modifiers: Set<types.KeyboardModifier>, keyName: string, description: KeyDescription, autoRepeat: boolean): Promise<void>;
keyup(modifiers: Set<types.KeyboardModifier>, keyName: string, description: KeyDescription): Promise<void>;
sendText(text: string): Promise<void>;
}
@ -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) {

View file

@ -59,12 +59,13 @@ export class RawKeyboardImpl implements input.RawKeyboard {
this._session = session;
}
async keydown(modifiers: Set<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number, autoRepeat: boolean, text: string | undefined): Promise<void> {
async keydown(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription, autoRepeat: boolean): Promise<void> {
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<types.KeyboardModifier>, code: string, keyCode: number, keyCodeWithoutLocation: number, key: string, location: number): Promise<void> {
async keyup(modifiers: Set<types.KeyboardModifier>, keyName: string, description: input.KeyDescription): Promise<void> {
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
});
}