Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ctrl_or_meta): add a universal ctrl-meta modifier #30572

Merged
merged 1 commit into from Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/api/class-elementhandle.md
Expand Up @@ -692,7 +692,7 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.

Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.

Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

Expand Down
3 changes: 2 additions & 1 deletion docs/src/api/class-frame.md
Expand Up @@ -1394,7 +1394,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.

Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.

Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

Expand Down
6 changes: 4 additions & 2 deletions docs/src/api/class-keyboard.md
Expand Up @@ -151,7 +151,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.

Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.

Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

Expand Down Expand Up @@ -227,7 +228,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.

Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.

Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

Expand Down
3 changes: 2 additions & 1 deletion docs/src/api/class-locator.md
Expand Up @@ -1761,7 +1761,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.

Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.

Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

Expand Down
3 changes: 2 additions & 1 deletion docs/src/api/class-page.md
Expand Up @@ -3013,7 +3013,8 @@ generate the text for. A superset of the [`param: key`] values can be found
`F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
`Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.

Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`, `ControlOrMeta`.
`ControlOrMeta` resolves to `Control` on Windows and Linux and to `Meta` on macOS.

Holding down `Shift` will type the text that corresponds to the [`param: key`] in the upper case.

Expand Down
5 changes: 3 additions & 2 deletions docs/src/api/params.md
Expand Up @@ -97,10 +97,11 @@ A point to use relative to the top-left corner of element padding box. If not sp
element.

## input-modifiers
- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"Meta"|"Shift">>>
- `modifiers` <[Array]<[KeyboardModifier]<"Alt"|"Control"|"ControlOrMeta"|"Meta"|"Shift">>>

Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
modifiers back. If not specified, currently pressed modifiers are used.
modifiers back. If not specified, currently pressed modifiers are used. "ControlOrMeta" resolves to "Control" on Windows
and Linux and to "Meta" on macOS.

## input-button
- `button` <[MouseButton]<"left"|"right"|"middle">>
Expand Down
16 changes: 16 additions & 0 deletions docs/src/input.md
Expand Up @@ -217,6 +217,10 @@ await page.getByText('Item').click({ button: 'right' });
// Shift + click
await page.getByText('Item').click({ modifiers: ['Shift'] });

// Ctrl + click or Windows and Linux
// Meta + click on macOS
await page.getByText('Item').click({ modifiers: ['ControlOrMeta'] });

// Hover over element
await page.getByText('Item').hover();

Expand All @@ -237,6 +241,10 @@ page.getByText("Item").click(new Locator.ClickOptions().setButton(MouseButton.RI
// Shift + click
page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.SHIFT)));

// Ctrl + click or Windows and Linux
// Meta + click on macOS
page.getByText("Item").click(new Locator.ClickOptions().setModifiers(Arrays.asList(KeyboardModifier.CONTROL_OR_META)));

// Hover over element
page.getByText("Item").hover();

Expand All @@ -257,6 +265,10 @@ await page.get_by_text("Item").click(button="right")
# Shift + click
await page.get_by_text("Item").click(modifiers=["Shift"])

# Ctrl + click or Windows and Linux
# Meta + click on macOS
await page.get_by_text("Item").click(modifiers=["ControlOrMeta"])

# Hover over element
await page.get_by_text("Item").hover()

Expand Down Expand Up @@ -297,6 +309,10 @@ await page.GetByText("Item").ClickAsync(new() { Button = MouseButton.Right });
// Shift + click
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.Shift } });

// Ctrl + click or Windows and Linux
// Meta + click on macOS
await page.GetByText("Item").ClickAsync(new() { Modifiers = new[] { KeyboardModifier.ControlOrMeta } });

// Hover over element
await page.GetByText("Item").HoverAsync();

Expand Down
16 changes: 8 additions & 8 deletions packages/playwright-core/src/protocol/validator.ts
Expand Up @@ -1342,7 +1342,7 @@ scheme.FrameClickParams = tObject({
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])),
Expand Down Expand Up @@ -1372,7 +1372,7 @@ scheme.FrameDblclickParams = tObject({
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])),
Expand Down Expand Up @@ -1450,7 +1450,7 @@ scheme.FrameHoverParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
timeout: tOptional(tNumber),
trial: tOptional(tBoolean),
Expand Down Expand Up @@ -1597,7 +1597,7 @@ scheme.FrameTapParams = tObject({
strict: tOptional(tBoolean),
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
timeout: tOptional(tNumber),
trial: tOptional(tBoolean),
Expand Down Expand Up @@ -1792,7 +1792,7 @@ scheme.ElementHandleCheckResult = tOptional(tObject({}));
scheme.ElementHandleClickParams = tObject({
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])),
Expand All @@ -1808,7 +1808,7 @@ scheme.ElementHandleContentFrameResult = tObject({
scheme.ElementHandleDblclickParams = tObject({
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
delay: tOptional(tNumber),
button: tOptional(tEnum(['left', 'right', 'middle'])),
Expand Down Expand Up @@ -1838,7 +1838,7 @@ scheme.ElementHandleGetAttributeResult = tObject({
});
scheme.ElementHandleHoverParams = tObject({
force: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
timeout: tOptional(tNumber),
trial: tOptional(tBoolean),
Expand Down Expand Up @@ -1962,7 +1962,7 @@ scheme.ElementHandleSetInputFilesResult = tOptional(tObject({}));
scheme.ElementHandleTapParams = tObject({
force: tOptional(tBoolean),
noWaitAfter: tOptional(tBoolean),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'Meta', 'Shift']))),
modifiers: tOptional(tArray(tEnum(['Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift']))),
position: tOptional(tType('Point')),
timeout: tOptional(tNumber),
trial: tOptional(tBoolean),
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/server/dom.ts
Expand Up @@ -449,11 +449,11 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
progress.throwIfAborted(); // Avoid action that has side-effects.
let restoreModifiers: types.KeyboardModifier[] | undefined;
if (options && options.modifiers)
restoreModifiers = await this._page.keyboard._ensureModifiers(options.modifiers);
restoreModifiers = await this._page.keyboard.ensureModifiers(options.modifiers);
progress.log(` performing ${actionName} action`);
await action(point);
if (restoreModifiers)
await this._page.keyboard._ensureModifiers(restoreModifiers);
await this._page.keyboard.ensureModifiers(restoreModifiers);
if (hitTargetInterceptionHandle) {
const stopHitTargetInterception = hitTargetInterceptionHandle.evaluate(h => h.stop()).catch(e => 'done' as const).finally(() => {
hitTargetInterceptionHandle?.dispose();
Expand Down
20 changes: 15 additions & 5 deletions packages/playwright-core/src/server/input.ts
Expand Up @@ -44,11 +44,9 @@ export class Keyboard {
private _pressedModifiers = new Set<types.KeyboardModifier>();
private _pressedKeys = new Set<string>();
private _raw: RawKeyboard;
private _page: Page;

constructor(raw: RawKeyboard, page: Page) {
constructor(raw: RawKeyboard) {
this._raw = raw;
this._page = page;
}

async down(key: string) {
Expand All @@ -61,7 +59,8 @@ export class Keyboard {
await this._raw.keydown(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location, autoRepeat, text);
}

private _keyDescriptionForString(keyString: string): KeyDescription {
private _keyDescriptionForString(str: string): KeyDescription {
const keyString = resolveSmartModifierString(str);
let description = usKeyboardLayout.get(keyString);
assert(description, `Unknown key: "${keyString}"`);
const shift = this._pressedModifiers.has('Shift');
Expand Down Expand Up @@ -126,7 +125,8 @@ export class Keyboard {
await this.up(tokens[i]);
}

async _ensureModifiers(modifiers: types.KeyboardModifier[]): Promise<types.KeyboardModifier[]> {
async ensureModifiers(mm: types.SmartKeyboardModifier[]): Promise<types.KeyboardModifier[]> {
const modifiers = mm.map(resolveSmartModifier);
for (const modifier of modifiers) {
if (!kModifiers.includes(modifier))
throw new Error('Unknown modifier ' + modifier);
Expand All @@ -148,6 +148,16 @@ export class Keyboard {
}
}

export function resolveSmartModifierString(key: string): string {
if (key === 'ControlOrMeta')
return process.platform === 'darwin' ? 'Meta' : 'Control';
return key;
}

export function resolveSmartModifier(m: types.SmartKeyboardModifier): types.KeyboardModifier {
return resolveSmartModifierString(m) as types.KeyboardModifier;
}

export interface RawMouse {
move(x: number, y: number, button: types.MouseButton | 'none', buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, forClick: boolean): Promise<void>;
down(x: number, y: number, button: types.MouseButton, buttons: Set<types.MouseButton>, modifiers: Set<types.KeyboardModifier>, clickCount: number): Promise<void>;
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/page.ts
Expand Up @@ -183,7 +183,7 @@ export class Page extends SdkObject {
this._delegate = delegate;
this._browserContext = browserContext;
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
this.keyboard = new input.Keyboard(delegate.rawKeyboard, this);
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
this.mouse = new input.Mouse(delegate.rawMouse, this);
this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this);
this._timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings);
Expand Down
9 changes: 5 additions & 4 deletions packages/playwright-core/src/server/recorder/utils.ts
Expand Up @@ -15,6 +15,7 @@
*/

import type { Frame } from '../frames';
import type { SmartKeyboardModifier } from '../types';
import type * as actions from './recorderActions';

export type MouseClickOptions = Parameters<Frame['click']>[2];
Expand All @@ -36,14 +37,14 @@ export function toClickOptions(action: actions.ClickAction): { method: 'click' |
return { method, options };
}

export function toModifiers(modifiers: number): ('Alt' | 'Control' | 'Meta' | 'Shift')[] {
const result: ('Alt' | 'Control' | 'Meta' | 'Shift')[] = [];
export function toModifiers(modifiers: number): SmartKeyboardModifier[] {
const result: SmartKeyboardModifier[] = [];
if (modifiers & 1)
result.push('Alt');
if (modifiers & 2)
result.push('Control');
result.push('ControlOrMeta');
if (modifiers & 4)
result.push('Meta');
result.push('ControlOrMeta');
if (modifiers & 8)
result.push('Shift');
return result;
Expand Down
3 changes: 2 additions & 1 deletion packages/playwright-core/src/server/types.ts
Expand Up @@ -105,10 +105,11 @@ export type ProxySettings = {
};

export type KeyboardModifier = 'Alt' | 'Control' | 'Meta' | 'Shift';
export type SmartKeyboardModifier = KeyboardModifier | 'ControlOrMeta';
export type MouseButton = 'left' | 'right' | 'middle';

export type PointerActionOptions = {
modifiers?: KeyboardModifier[];
modifiers?: SmartKeyboardModifier[];
position?: Point;
};

Expand Down