2017-07-18 01:49:52 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2017-07-19 03:53:00 +00:00
|
|
|
const helper = require('./helper');
|
|
|
|
|
2017-07-18 01:49:52 +00:00
|
|
|
class Keyboard {
|
|
|
|
/**
|
|
|
|
* @param {!Connection} client
|
|
|
|
*/
|
|
|
|
constructor(client) {
|
|
|
|
this._client = client;
|
2017-07-25 21:35:03 +00:00
|
|
|
this._modifiers = 0;
|
2017-07-31 19:05:46 +00:00
|
|
|
this._pressedKeys = new Set();
|
2017-07-18 01:49:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} key
|
2017-07-31 19:05:46 +00:00
|
|
|
* @param {{text: (string|undefined)}} options
|
2017-07-18 01:49:52 +00:00
|
|
|
* @return {!Promise}
|
|
|
|
*/
|
2017-07-19 21:27:01 +00:00
|
|
|
async down(key, options) {
|
2017-07-31 19:05:46 +00:00
|
|
|
let {text} = options || {};
|
|
|
|
let autoRepeat = this._pressedKeys.has(key);
|
|
|
|
this._pressedKeys.add(key);
|
2017-07-25 21:35:03 +00:00
|
|
|
this._modifiers |= this._modifierBit(key);
|
2017-07-18 01:49:52 +00:00
|
|
|
await this._client.send('Input.dispatchKeyEvent', {
|
|
|
|
type: text ? 'keyDown' : 'rawKeyDown',
|
2017-07-25 21:35:03 +00:00
|
|
|
modifiers: this._modifiers,
|
2017-07-18 01:49:52 +00:00
|
|
|
windowsVirtualKeyCode: codeForKey(key),
|
|
|
|
key: key,
|
|
|
|
text: text,
|
2017-07-31 19:05:46 +00:00
|
|
|
unmodifiedText: text,
|
|
|
|
autoRepeat
|
2017-07-18 01:49:52 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-25 21:35:03 +00:00
|
|
|
* @param {string} key
|
|
|
|
* @return {number}
|
2017-07-18 01:49:52 +00:00
|
|
|
*/
|
2017-07-25 21:35:03 +00:00
|
|
|
_modifierBit(key) {
|
|
|
|
if (key === 'Alt')
|
|
|
|
return 1;
|
|
|
|
if (key === 'Control')
|
|
|
|
return 2;
|
|
|
|
if (key === 'Meta')
|
|
|
|
return 4;
|
|
|
|
if (key === 'Shift')
|
|
|
|
return 8;
|
|
|
|
return 0;
|
2017-07-18 01:49:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} key
|
|
|
|
* @return {!Promise}
|
|
|
|
*/
|
2017-07-19 21:27:01 +00:00
|
|
|
async up(key) {
|
2017-07-25 21:35:03 +00:00
|
|
|
this._modifiers &= ~this._modifierBit(key);
|
2017-07-31 19:05:46 +00:00
|
|
|
this._pressedKeys.delete(key);
|
2017-07-18 01:49:52 +00:00
|
|
|
await this._client.send('Input.dispatchKeyEvent', {
|
|
|
|
type: 'keyUp',
|
2017-07-25 21:35:03 +00:00
|
|
|
modifiers: this._modifiers,
|
2017-07-18 01:49:52 +00:00
|
|
|
key,
|
|
|
|
windowsVirtualKeyCode: codeForKey(key),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} char
|
|
|
|
* @return {!Promise}
|
|
|
|
*/
|
|
|
|
async sendCharacter(char) {
|
|
|
|
await this._client.send('Input.dispatchKeyEvent', {
|
|
|
|
type: 'char',
|
2017-07-25 21:35:03 +00:00
|
|
|
modifiers: this._modifiers,
|
2017-07-18 01:49:52 +00:00
|
|
|
text: char,
|
|
|
|
key: char,
|
|
|
|
unmodifiedText: char
|
|
|
|
});
|
|
|
|
}
|
2017-07-25 21:35:03 +00:00
|
|
|
}
|
2017-07-18 01:49:52 +00:00
|
|
|
|
2017-07-25 21:35:03 +00:00
|
|
|
class Mouse {
|
2017-07-18 01:49:52 +00:00
|
|
|
/**
|
2017-07-25 21:35:03 +00:00
|
|
|
* @param {!Connection} 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
|
|
|
|
* @return {!Promise}
|
|
|
|
*/
|
|
|
|
async move(x, y) {
|
|
|
|
this._x = x;
|
|
|
|
this._y = y;
|
|
|
|
await this._client.send('Input.dispatchMouseEvent', {
|
|
|
|
type: 'mouseMoved',
|
|
|
|
button: this._button,
|
|
|
|
x, y,
|
|
|
|
modifiers: this._keyboard._modifiers
|
|
|
|
});
|
2017-07-26 00:03:13 +00:00
|
|
|
await this._doubleRaf();
|
2017-07-25 21:35:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-07-25 23:05:23 +00:00
|
|
|
* @param {number} x
|
|
|
|
* @param {number} y
|
2017-07-25 21:35:03 +00:00
|
|
|
* @param {!Object=} options
|
|
|
|
*/
|
2017-07-25 23:05:23 +00:00
|
|
|
async click(x, y, options) {
|
2017-07-26 00:03:13 +00:00
|
|
|
this.move(x, y);
|
|
|
|
this.down(options);
|
2017-08-01 01:18:15 +00:00
|
|
|
if (options && options.delay)
|
|
|
|
await new Promise(f => setTimeout(f, options.delay));
|
2017-07-25 21:35:03 +00:00
|
|
|
await this.up(options);
|
2017-07-26 00:03:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async _doubleRaf() {
|
2017-07-25 23:05:23 +00:00
|
|
|
// This is a hack for now, to make clicking less race-prone. See crbug.com/747647
|
|
|
|
await this._client.send('Runtime.evaluate', {
|
2017-07-26 00:03:13 +00:00
|
|
|
expression: 'new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))',
|
2017-07-25 23:05:23 +00:00
|
|
|
awaitPromise: true,
|
|
|
|
returnByValue: true
|
|
|
|
});
|
2017-07-25 21:35:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!Object=} options
|
2017-07-18 01:49:52 +00:00
|
|
|
*/
|
2017-07-25 21:35:03 +00:00
|
|
|
async down(options) {
|
|
|
|
if (!options)
|
|
|
|
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)
|
|
|
|
});
|
2017-07-26 00:03:13 +00:00
|
|
|
await this._doubleRaf();
|
2017-07-25 21:35:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!Object=} options
|
|
|
|
*/
|
|
|
|
async up(options) {
|
|
|
|
if (!options)
|
|
|
|
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)
|
|
|
|
});
|
2017-07-26 00:03:13 +00:00
|
|
|
await this._doubleRaf();
|
2017-07-18 01:49:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let keys = {
|
|
|
|
'Cancel': 3,
|
|
|
|
'Help': 6,
|
|
|
|
'Backspace': 8,
|
|
|
|
'Tab': 9,
|
|
|
|
'Clear': 12,
|
|
|
|
'Enter': 13,
|
|
|
|
'Shift': 16,
|
|
|
|
'Control': 17,
|
|
|
|
'Alt': 18,
|
|
|
|
'Pause': 19,
|
|
|
|
'CapsLock': 20,
|
|
|
|
'Escape': 27,
|
|
|
|
'Convert': 28,
|
|
|
|
'NonConvert': 29,
|
|
|
|
'Accept': 30,
|
|
|
|
'ModeChange': 31,
|
|
|
|
'PageUp': 33,
|
|
|
|
'PageDown': 34,
|
|
|
|
'End': 35,
|
|
|
|
'Home': 36,
|
|
|
|
'ArrowLeft': 37,
|
|
|
|
'ArrowUp': 38,
|
|
|
|
'ArrowRight': 39,
|
|
|
|
'ArrowDown': 40,
|
|
|
|
'Select': 41,
|
|
|
|
'Print': 42,
|
|
|
|
'Execute': 43,
|
|
|
|
'PrintScreen': 44,
|
|
|
|
'Insert': 45,
|
|
|
|
'Delete': 46,
|
|
|
|
')': 48,
|
|
|
|
'!': 49,
|
|
|
|
'@': 50,
|
|
|
|
'#': 51,
|
|
|
|
'$': 52,
|
|
|
|
'%': 53,
|
|
|
|
'^': 54,
|
|
|
|
'&': 55,
|
|
|
|
'*': 56,
|
|
|
|
'(': 57,
|
|
|
|
'Meta': 91,
|
|
|
|
'ContextMenu': 93,
|
|
|
|
'F1': 112,
|
|
|
|
'F2': 113,
|
|
|
|
'F3': 114,
|
|
|
|
'F4': 115,
|
|
|
|
'F5': 116,
|
|
|
|
'F6': 117,
|
|
|
|
'F7': 118,
|
|
|
|
'F8': 119,
|
|
|
|
'F9': 120,
|
|
|
|
'F10': 121,
|
|
|
|
'F11': 122,
|
|
|
|
'F12': 123,
|
|
|
|
'F13': 124,
|
|
|
|
'F14': 125,
|
|
|
|
'F15': 126,
|
|
|
|
'F16': 127,
|
|
|
|
'F17': 128,
|
|
|
|
'F18': 129,
|
|
|
|
'F19': 130,
|
|
|
|
'F20': 131,
|
|
|
|
'F21': 132,
|
|
|
|
'F22': 133,
|
|
|
|
'F23': 134,
|
|
|
|
'F24': 135,
|
|
|
|
'NumLock': 144,
|
|
|
|
'ScrollLock': 145,
|
2017-07-28 18:38:30 +00:00
|
|
|
'AudioVolumeMute': 173,
|
|
|
|
'AudioVolumeDown': 174,
|
|
|
|
'AudioVolumeUp': 175,
|
|
|
|
'MediaTrackNext': 176,
|
|
|
|
'MediaTrackPrevious': 177,
|
|
|
|
'MediaStop': 178,
|
|
|
|
'MediaPlayPause': 179,
|
2017-07-18 01:49:52 +00:00
|
|
|
';': 186,
|
|
|
|
':': 186,
|
|
|
|
'=': 187,
|
|
|
|
'+': 187,
|
|
|
|
',': 188,
|
|
|
|
'<': 188,
|
|
|
|
'-': 189,
|
|
|
|
'_': 189,
|
|
|
|
'.': 190,
|
|
|
|
'>': 190,
|
|
|
|
'/': 191,
|
|
|
|
'?': 191,
|
|
|
|
'`': 192,
|
|
|
|
'~': 192,
|
|
|
|
'[': 219,
|
|
|
|
'{': 219,
|
|
|
|
'\\': 220,
|
|
|
|
'|': 220,
|
|
|
|
']': 221,
|
|
|
|
'}': 221,
|
|
|
|
'\'': 222,
|
|
|
|
'"': 222,
|
|
|
|
'AltGraph': 225,
|
|
|
|
'Attn': 246,
|
|
|
|
'CrSel': 247,
|
|
|
|
'ExSel': 248,
|
|
|
|
'EraseEof': 249,
|
|
|
|
'Play': 250,
|
|
|
|
'ZoomOut': 251
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} key
|
|
|
|
* @return {number}
|
|
|
|
*/
|
|
|
|
function codeForKey(key) {
|
|
|
|
if (keys[key])
|
|
|
|
return keys[key];
|
|
|
|
if (key.length === 1)
|
|
|
|
return key.toUpperCase().charCodeAt(0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-28 18:38:30 +00:00
|
|
|
module.exports = { Keyboard, Mouse };
|
2017-07-19 03:53:00 +00:00
|
|
|
helper.tracePublicAPI(Keyboard);
|
2017-07-25 21:35:03 +00:00
|
|
|
helper.tracePublicAPI(Mouse);
|