mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
6a8865cd85
This patch starts generating input events for `keyboard.down`, addressing bullet 2 of #723. With this patch, there's no longer any difference between `keboard.press('a')` and `keyboard.press('a', {text: 'a'})`. BREAKING CHANGE: `keyboard.down('a')` starts generating input event (wasn't the case before). References #723
430 lines
13 KiB
JavaScript
430 lines
13 KiB
JavaScript
/**
|
|
* Copyright 2017 Google Inc. All rights reserved.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
const {helper} = require('./helper');
|
|
|
|
class Keyboard {
|
|
/**
|
|
* @param {!Puppeteer.Session} client
|
|
*/
|
|
constructor(client) {
|
|
this._client = client;
|
|
this._modifiers = 0;
|
|
this._pressedKeys = new Set();
|
|
}
|
|
|
|
/**
|
|
* @param {string} key
|
|
* @param {{text: string}=} options
|
|
*/
|
|
async down(key, options = {text: undefined}) {
|
|
let { text } = options;
|
|
// If the key is a single character, and no modifiers are pressed except shift, a keypress should be generated by default.
|
|
if (text === undefined)
|
|
text = (key.length === 1 && !(this._modifiers & ~8)) ? key : '';
|
|
const autoRepeat = this._pressedKeys.has(key);
|
|
this._pressedKeys.add(key);
|
|
this._modifiers |= this._modifierBit(key);
|
|
await this._client.send('Input.dispatchKeyEvent', {
|
|
type: text ? 'keyDown' : 'rawKeyDown',
|
|
modifiers: this._modifiers,
|
|
windowsVirtualKeyCode: keyCodeForKey(key),
|
|
code: codeForKey(key),
|
|
key,
|
|
text,
|
|
unmodifiedText: text,
|
|
autoRepeat
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {string} key
|
|
* @return {number}
|
|
*/
|
|
_modifierBit(key) {
|
|
if (key === 'Alt')
|
|
return 1;
|
|
if (key === 'Control')
|
|
return 2;
|
|
if (key === 'Meta')
|
|
return 4;
|
|
if (key === 'Shift')
|
|
return 8;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @param {string} key
|
|
*/
|
|
async up(key) {
|
|
this._modifiers &= ~this._modifierBit(key);
|
|
this._pressedKeys.delete(key);
|
|
await this._client.send('Input.dispatchKeyEvent', {
|
|
type: 'keyUp',
|
|
modifiers: this._modifiers,
|
|
key,
|
|
windowsVirtualKeyCode: keyCodeForKey(key),
|
|
code: codeForKey(key)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {string} char
|
|
*/
|
|
async sendCharacter(char) {
|
|
await this._client.send('Input.dispatchKeyEvent', {
|
|
type: 'char',
|
|
modifiers: this._modifiers,
|
|
text: char,
|
|
key: char,
|
|
unmodifiedText: char
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {string} text
|
|
* @param {{delay: (number|undefined)}=} options
|
|
*/
|
|
async type(text, options) {
|
|
let delay = 0;
|
|
if (options && options.delay)
|
|
delay = options.delay;
|
|
for (const char of text) {
|
|
await this.press(char, {text: char, delay});
|
|
if (delay)
|
|
await new Promise(f => setTimeout(f, delay));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} key
|
|
* @param {!Object=} options
|
|
*/
|
|
async press(key, options) {
|
|
await this.down(key, options);
|
|
if (options && options.delay)
|
|
await new Promise(f => setTimeout(f, options.delay));
|
|
await this.up(key);
|
|
}
|
|
}
|
|
|
|
class Mouse {
|
|
/**
|
|
* @param {Puppeteer.Session} client
|
|
* @param {!Keyboard} keyboard
|
|
*/
|
|
constructor(client, keyboard) {
|
|
this._client = client;
|
|
this._keyboard = keyboard;
|
|
this._x = 0;
|
|
this._y = 0;
|
|
this._button = 'none';
|
|
}
|
|
|
|
/**
|
|
* @param {number} x
|
|
* @param {number} y
|
|
* @param {Object=} options
|
|
* @return {!Promise}
|
|
*/
|
|
async move(x, y, options = {}) {
|
|
const fromX = this._x, fromY = this._y;
|
|
this._x = x;
|
|
this._y = y;
|
|
const steps = options.steps || 1;
|
|
for (let i = 1; i <= steps; i++) {
|
|
await this._client.send('Input.dispatchMouseEvent', {
|
|
type: 'mouseMoved',
|
|
button: this._button,
|
|
x: fromX + (this._x - fromX) * (i / steps),
|
|
y: fromY + (this._y - fromY) * (i / steps),
|
|
modifiers: this._keyboard._modifiers
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} x
|
|
* @param {number} y
|
|
* @param {!Object=} options
|
|
*/
|
|
async click(x, y, options = {}) {
|
|
this.move(x, y);
|
|
this.down(options);
|
|
if (typeof options.delay === 'number')
|
|
await new Promise(f => setTimeout(f, options.delay));
|
|
await this.up(options);
|
|
}
|
|
|
|
/**
|
|
* @param {!Object=} options
|
|
*/
|
|
async down(options = {}) {
|
|
this._button = (options.button || 'left');
|
|
await this._client.send('Input.dispatchMouseEvent', {
|
|
type: 'mousePressed',
|
|
button: this._button,
|
|
x: this._x,
|
|
y: this._y,
|
|
modifiers: this._keyboard._modifiers,
|
|
clickCount: (options.clickCount || 1)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {!Object=} options
|
|
*/
|
|
async up(options = {}) {
|
|
this._button = 'none';
|
|
await this._client.send('Input.dispatchMouseEvent', {
|
|
type: 'mouseReleased',
|
|
button: (options.button || 'left'),
|
|
x: this._x,
|
|
y: this._y,
|
|
modifiers: this._keyboard._modifiers,
|
|
clickCount: (options.clickCount || 1)
|
|
});
|
|
}
|
|
}
|
|
|
|
class Touchscreen {
|
|
/**
|
|
* @param {Puppeteer.Session} client
|
|
* @param {Keyboard} keyboard
|
|
*/
|
|
constructor(client, keyboard) {
|
|
this._client = client;
|
|
this._keyboard = keyboard;
|
|
}
|
|
|
|
/**
|
|
* @param {number} x
|
|
* @param {number} y
|
|
*/
|
|
async tap(x, y) {
|
|
const touchPoints = [{x: Math.round(x), y: Math.round(y)}];
|
|
await this._client.send('Input.dispatchTouchEvent', {
|
|
type: 'touchStart',
|
|
touchPoints,
|
|
modifiers: this._keyboard._modifiers
|
|
});
|
|
await this._client.send('Input.dispatchTouchEvent', {
|
|
type: 'touchEnd',
|
|
touchPoints: [],
|
|
modifiers: this._keyboard._modifiers
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @type {Object<string, {keyCode: number, code: string}>}
|
|
*/
|
|
const keys = {
|
|
'Cancel': {keyCode: 3, code: 'Abort'},
|
|
'Help': {keyCode: 6, code: 'Help'},
|
|
'Backspace': {keyCode: 8, code: 'Backspace'},
|
|
'Tab': {keyCode: 9, code: 'Tab'},
|
|
'Clear': {keyCode: 12, code: ''},
|
|
'Enter': {keyCode: 13, code: 'Enter'},
|
|
'Shift': {keyCode: 16, code: 'ShiftLeft'},
|
|
'Control': {keyCode: 17, code: 'ControlLeft'},
|
|
'Alt': {keyCode: 18, code: 'AltLeft'},
|
|
'Pause': {keyCode: 19, code: 'Pause'},
|
|
'CapsLock': {keyCode: 20, code: 'CapsLock'},
|
|
'Escape': {keyCode: 27, code: 'Escape'},
|
|
'Convert': {keyCode: 28, code: 'Convert'},
|
|
'NonConvert': {keyCode: 29, code: 'NonConvert'},
|
|
'Accept': {keyCode: 30, code: ''},
|
|
'ModeChange': {keyCode: 31, code: ''},
|
|
'PageUp': {keyCode: 33, code: 'PageUp'},
|
|
'PageDown': {keyCode: 34, code: 'PageDown'},
|
|
'End': {keyCode: 35, code: 'End'},
|
|
'Home': {keyCode: 36, code: 'Home'},
|
|
'ArrowLeft': {keyCode: 37, code: 'ArrowLeft'},
|
|
'ArrowUp': {keyCode: 38, code: 'ArrowUp'},
|
|
'ArrowRight': {keyCode: 39, code: 'ArrowRight'},
|
|
'ArrowDown': {keyCode: 40, code: 'ArrowDown'},
|
|
'Select': {keyCode: 41, code: 'Select'},
|
|
'Print': {keyCode: 42, code: ''},
|
|
'Execute': {keyCode: 43, code: 'Open'},
|
|
'PrintScreen': {keyCode: 44, code: 'PrintScreen'},
|
|
'Insert': {keyCode: 45, code: 'Insert'},
|
|
'Delete': {keyCode: 46, code: 'Delete'},
|
|
')': {keyCode: 48, code: 'Digit0'},
|
|
'!': {keyCode: 49, code: 'Digit1'},
|
|
'@': {keyCode: 50, code: 'Digit2'},
|
|
'#': {keyCode: 51, code: 'Digit3'},
|
|
'$': {keyCode: 52, code: 'Digit4'},
|
|
'%': {keyCode: 53, code: 'Digit5'},
|
|
'^': {keyCode: 54, code: 'Digit6'},
|
|
'&': {keyCode: 55, code: 'Digit7'},
|
|
'*': {keyCode: 56, code: 'Digit8'},
|
|
'(': {keyCode: 57, code: 'Digit9'},
|
|
'Meta': {keyCode: 91, code: 'MetaLeft'},
|
|
'ContextMenu': {keyCode: 93, code: 'ContextMenu'},
|
|
'F1': {keyCode: 112, code: 'F1'},
|
|
'F2': {keyCode: 113, code: 'F2'},
|
|
'F3': {keyCode: 114, code: 'F3'},
|
|
'F4': {keyCode: 115, code: 'F4'},
|
|
'F5': {keyCode: 116, code: 'F5'},
|
|
'F6': {keyCode: 117, code: 'F6'},
|
|
'F7': {keyCode: 118, code: 'F7'},
|
|
'F8': {keyCode: 119, code: 'F8'},
|
|
'F9': {keyCode: 120, code: 'F9'},
|
|
'F10': {keyCode: 121, code: 'F10'},
|
|
'F11': {keyCode: 122, code: 'F11'},
|
|
'F12': {keyCode: 123, code: 'F12'},
|
|
'F13': {keyCode: 124, code: 'F13'},
|
|
'F14': {keyCode: 125, code: 'F14'},
|
|
'F15': {keyCode: 126, code: 'F15'},
|
|
'F16': {keyCode: 127, code: 'F16'},
|
|
'F17': {keyCode: 128, code: 'F17'},
|
|
'F18': {keyCode: 129, code: 'F18'},
|
|
'F19': {keyCode: 130, code: 'F19'},
|
|
'F20': {keyCode: 131, code: 'F20'},
|
|
'F21': {keyCode: 132, code: 'F21'},
|
|
'F22': {keyCode: 133, code: 'F22'},
|
|
'F23': {keyCode: 134, code: 'F23'},
|
|
'F24': {keyCode: 135, code: 'F24'},
|
|
'NumLock': {keyCode: 144, code: 'NumLock'},
|
|
'ScrollLock': {keyCode: 145, code: 'ScrollLock'},
|
|
'AudioVolumeMute': {keyCode: 173, code: 'AudioVolumeMute'},
|
|
'AudioVolumeDown': {keyCode: 174, code: 'AudioVolumeDown'},
|
|
'AudioVolumeUp': {keyCode: 175, code: 'AudioVolumeUp'},
|
|
'MediaTrackNext': {keyCode: 176, code: 'MediaTrackNext'},
|
|
'MediaTrackPrevious': {keyCode: 177, code: 'MediaTrackPrevious'},
|
|
'MediaStop': {keyCode: 178, code: 'MediaStop'},
|
|
'MediaPlayPause': {keyCode: 179, code: 'MediaPlayPause'},
|
|
';': {keyCode: 186, code: 'Semicolon'},
|
|
':': {keyCode: 186, code: 'Semicolon'},
|
|
'=': {keyCode: 187, code: 'Equal'},
|
|
'+': {keyCode: 187, code: 'Equal'},
|
|
',': {keyCode: 188, code: 'Comma'},
|
|
'<': {keyCode: 188, code: 'Comma'},
|
|
'-': {keyCode: 189, code: 'Minus'},
|
|
'_': {keyCode: 189, code: 'Minus'},
|
|
'.': {keyCode: 190, code: 'Period'},
|
|
'>': {keyCode: 190, code: 'Period'},
|
|
'/': {keyCode: 191, code: 'Slash'},
|
|
'?': {keyCode: 191, code: 'Slash'},
|
|
'`': {keyCode: 192, code: 'Backquote'},
|
|
'~': {keyCode: 192, code: 'Backquote'},
|
|
'[': {keyCode: 219, code: 'BracketLeft'},
|
|
'{': {keyCode: 219, code: 'BracketLeft'},
|
|
'\\': {keyCode: 220, code: 'Backslash'},
|
|
'|': {keyCode: 220, code: 'Backslash'},
|
|
']': {keyCode: 221, code: 'BracketRight'},
|
|
'}': {keyCode: 221, code: 'BracketRight'},
|
|
'\'': {keyCode: 222, code: 'Quote'},
|
|
'"': {keyCode: 222, code: 'Quote'},
|
|
'AltGraph': {keyCode: 225, code: 'AltGraph'},
|
|
'Attn': {keyCode: 246, code: ''},
|
|
'CrSel': {keyCode: 247, code: 'Props'},
|
|
'ExSel': {keyCode: 248, code: ''},
|
|
'EraseEof': {keyCode: 249, code: ''},
|
|
'Play': {keyCode: 250, code: ''},
|
|
'ZoomOut': {keyCode: 251, code: ''},
|
|
'0': { keyCode: 48, code: 'Digit0'},
|
|
'1': { keyCode: 49, code: 'Digit1'},
|
|
'2': { keyCode: 50, code: 'Digit2'},
|
|
'3': { keyCode: 51, code: 'Digit3'},
|
|
'4': { keyCode: 52, code: 'Digit4'},
|
|
'5': { keyCode: 53, code: 'Digit5'},
|
|
'6': { keyCode: 54, code: 'Digit6'},
|
|
'7': { keyCode: 55, code: 'Digit7'},
|
|
'8': { keyCode: 56, code: 'Digit8'},
|
|
'9': { keyCode: 57, code: 'Digit9'},
|
|
'q': { keyCode: 81, code: 'KeyQ'},
|
|
'w': { keyCode: 87, code: 'KeyW'},
|
|
'e': { keyCode: 69, code: 'KeyE'},
|
|
'r': { keyCode: 82, code: 'KeyR'},
|
|
't': { keyCode: 84, code: 'KeyT'},
|
|
'y': { keyCode: 89, code: 'KeyY'},
|
|
'u': { keyCode: 85, code: 'KeyU'},
|
|
'i': { keyCode: 73, code: 'KeyI'},
|
|
'o': { keyCode: 79, code: 'KeyO'},
|
|
'p': { keyCode: 80, code: 'KeyP'},
|
|
'a': { keyCode: 65, code: 'KeyA'},
|
|
's': { keyCode: 83, code: 'KeyS'},
|
|
'd': { keyCode: 68, code: 'KeyD'},
|
|
'f': { keyCode: 70, code: 'KeyF'},
|
|
'g': { keyCode: 71, code: 'KeyG'},
|
|
'h': { keyCode: 72, code: 'KeyH'},
|
|
'j': { keyCode: 74, code: 'KeyJ'},
|
|
'k': { keyCode: 75, code: 'KeyK'},
|
|
'l': { keyCode: 76, code: 'KeyL'},
|
|
'z': { keyCode: 90, code: 'KeyZ'},
|
|
'x': { keyCode: 88, code: 'KeyX'},
|
|
'c': { keyCode: 67, code: 'KeyC'},
|
|
'v': { keyCode: 86, code: 'KeyV'},
|
|
'b': { keyCode: 66, code: 'KeyB'},
|
|
'n': { keyCode: 78, code: 'KeyN'},
|
|
'm': { keyCode: 77, code: 'KeyM'},
|
|
'Q': { keyCode: 81, code: 'KeyQ'},
|
|
'W': { keyCode: 87, code: 'KeyW'},
|
|
'E': { keyCode: 69, code: 'KeyE'},
|
|
'R': { keyCode: 82, code: 'KeyR'},
|
|
'T': { keyCode: 84, code: 'KeyT'},
|
|
'Y': { keyCode: 89, code: 'KeyY'},
|
|
'U': { keyCode: 85, code: 'KeyU'},
|
|
'I': { keyCode: 73, code: 'KeyI'},
|
|
'O': { keyCode: 79, code: 'KeyO'},
|
|
'P': { keyCode: 80, code: 'KeyP'},
|
|
'A': { keyCode: 65, code: 'KeyA'},
|
|
'S': { keyCode: 83, code: 'KeyS'},
|
|
'D': { keyCode: 68, code: 'KeyD'},
|
|
'F': { keyCode: 70, code: 'KeyF'},
|
|
'G': { keyCode: 71, code: 'KeyG'},
|
|
'H': { keyCode: 72, code: 'KeyH'},
|
|
'J': { keyCode: 74, code: 'KeyJ'},
|
|
'K': { keyCode: 75, code: 'KeyK'},
|
|
'L': { keyCode: 76, code: 'KeyL'},
|
|
'Z': { keyCode: 90, code: 'KeyZ'},
|
|
'X': { keyCode: 88, code: 'KeyX'},
|
|
'C': { keyCode: 67, code: 'KeyC'},
|
|
'V': { keyCode: 86, code: 'KeyV'},
|
|
'B': { keyCode: 66, code: 'KeyB'},
|
|
'N': { keyCode: 78, code: 'KeyN'},
|
|
'M': { keyCode: 77, code: 'KeyM'}
|
|
};
|
|
|
|
/**
|
|
* @param {string} key
|
|
* @return {number}
|
|
*/
|
|
function keyCodeForKey(key) {
|
|
if (keys[key])
|
|
return keys[key].keyCode;
|
|
if (key.length === 1)
|
|
return key.toUpperCase().charCodeAt(0);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @param {string} key
|
|
* @return {string}
|
|
*/
|
|
function codeForKey(key) {
|
|
if (!keys[key])
|
|
return '';
|
|
return keys[key].code;
|
|
}
|
|
|
|
module.exports = { Keyboard, Mouse, Touchscreen};
|
|
helper.tracePublicAPI(Keyboard);
|
|
helper.tracePublicAPI(Mouse);
|
|
helper.tracePublicAPI(Touchscreen);
|