1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | const {helper, assert} = require('./helper');
|
18 | const keyDefinitions = require('./USKeyboardLayout');
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | class Keyboard {
|
30 | |
31 |
|
32 |
|
33 | constructor(client) {
|
34 | this._client = client;
|
35 | this._modifiers = 0;
|
36 | this._pressedKeys = new Set();
|
37 | }
|
38 |
|
39 | |
40 |
|
41 |
|
42 |
|
43 | down(key, options = { text: undefined }) {return (fn => {
|
44 | const gen = fn.call(this);
|
45 | return new Promise((resolve, reject) => {
|
46 | function step(key, arg) {
|
47 | let info, value;
|
48 | try {
|
49 | info = gen[key](arg);
|
50 | value = info.value;
|
51 | } catch (error) {
|
52 | reject(error);
|
53 | return;
|
54 | }
|
55 | if (info.done) {
|
56 | resolve(value);
|
57 | } else {
|
58 | return Promise.resolve(value).then(
|
59 | value => {
|
60 | step('next', value);
|
61 | },
|
62 | err => {
|
63 | step('throw', err);
|
64 | });
|
65 | }
|
66 | }
|
67 | return step('next');
|
68 | });
|
69 | })(function*(){
|
70 | const description = this._keyDescriptionForString(key);
|
71 |
|
72 | const autoRepeat = this._pressedKeys.has(description.code);
|
73 | this._pressedKeys.add(description.code);
|
74 | this._modifiers |= this._modifierBit(description.key);
|
75 |
|
76 | const text = options.text === undefined ? description.text : options.text;
|
77 | (yield this._client.send('Input.dispatchKeyEvent', {
|
78 | type: text ? 'keyDown' : 'rawKeyDown',
|
79 | modifiers: this._modifiers,
|
80 | windowsVirtualKeyCode: description.keyCode,
|
81 | code: description.code,
|
82 | key: description.key,
|
83 | text: text,
|
84 | unmodifiedText: text,
|
85 | autoRepeat,
|
86 | location: description.location,
|
87 | isKeypad: description.location === 3
|
88 | }));
|
89 | });}
|
90 |
|
91 | |
92 |
|
93 |
|
94 |
|
95 | _modifierBit(key) {
|
96 | if (key === 'Alt')
|
97 | return 1;
|
98 | if (key === 'Control')
|
99 | return 2;
|
100 | if (key === 'Meta')
|
101 | return 4;
|
102 | if (key === 'Shift')
|
103 | return 8;
|
104 | return 0;
|
105 | }
|
106 |
|
107 | |
108 |
|
109 |
|
110 |
|
111 | _keyDescriptionForString(keyString) {
|
112 | const shift = this._modifiers & 8;
|
113 | const description = {
|
114 | key: '',
|
115 | keyCode: 0,
|
116 | code: '',
|
117 | text: '',
|
118 | location: 0
|
119 | };
|
120 |
|
121 | const definition = keyDefinitions[keyString];
|
122 | assert(definition, `Unknown key: "${keyString}"`);
|
123 |
|
124 | if (definition.key)
|
125 | description.key = definition.key;
|
126 | if (shift && definition.shiftKey)
|
127 | description.key = definition.shiftKey;
|
128 |
|
129 | if (definition.keyCode)
|
130 | description.keyCode = definition.keyCode;
|
131 | if (shift && definition.shiftKeyCode)
|
132 | description.keyCode = definition.shiftKeyCode;
|
133 |
|
134 | if (definition.code)
|
135 | description.code = definition.code;
|
136 |
|
137 | if (definition.location)
|
138 | description.location = definition.location;
|
139 |
|
140 | if (description.key.length === 1)
|
141 | description.text = description.key;
|
142 |
|
143 | if (definition.text)
|
144 | description.text = definition.text;
|
145 | if (shift && definition.shiftText)
|
146 | description.text = definition.shiftText;
|
147 |
|
148 |
|
149 | if (this._modifiers & ~8)
|
150 | description.text = '';
|
151 |
|
152 | return description;
|
153 | }
|
154 |
|
155 | |
156 |
|
157 |
|
158 | up(key) {return (fn => {
|
159 | const gen = fn.call(this);
|
160 | return new Promise((resolve, reject) => {
|
161 | function step(key, arg) {
|
162 | let info, value;
|
163 | try {
|
164 | info = gen[key](arg);
|
165 | value = info.value;
|
166 | } catch (error) {
|
167 | reject(error);
|
168 | return;
|
169 | }
|
170 | if (info.done) {
|
171 | resolve(value);
|
172 | } else {
|
173 | return Promise.resolve(value).then(
|
174 | value => {
|
175 | step('next', value);
|
176 | },
|
177 | err => {
|
178 | step('throw', err);
|
179 | });
|
180 | }
|
181 | }
|
182 | return step('next');
|
183 | });
|
184 | })(function*(){
|
185 | const description = this._keyDescriptionForString(key);
|
186 |
|
187 | this._modifiers &= ~this._modifierBit(description.key);
|
188 | this._pressedKeys.delete(description.code);
|
189 | (yield this._client.send('Input.dispatchKeyEvent', {
|
190 | type: 'keyUp',
|
191 | modifiers: this._modifiers,
|
192 | key: description.key,
|
193 | windowsVirtualKeyCode: description.keyCode,
|
194 | code: description.code,
|
195 | location: description.location
|
196 | }));
|
197 | });}
|
198 |
|
199 | |
200 |
|
201 |
|
202 | sendCharacter(char) {return (fn => {
|
203 | const gen = fn.call(this);
|
204 | return new Promise((resolve, reject) => {
|
205 | function step(key, arg) {
|
206 | let info, value;
|
207 | try {
|
208 | info = gen[key](arg);
|
209 | value = info.value;
|
210 | } catch (error) {
|
211 | reject(error);
|
212 | return;
|
213 | }
|
214 | if (info.done) {
|
215 | resolve(value);
|
216 | } else {
|
217 | return Promise.resolve(value).then(
|
218 | value => {
|
219 | step('next', value);
|
220 | },
|
221 | err => {
|
222 | step('throw', err);
|
223 | });
|
224 | }
|
225 | }
|
226 | return step('next');
|
227 | });
|
228 | })(function*(){
|
229 | (yield this._client.send('Input.insertText', {text: char}));
|
230 | });}
|
231 |
|
232 | |
233 |
|
234 |
|
235 |
|
236 | type(text, options) {return (fn => {
|
237 | const gen = fn.call(this);
|
238 | return new Promise((resolve, reject) => {
|
239 | function step(key, arg) {
|
240 | let info, value;
|
241 | try {
|
242 | info = gen[key](arg);
|
243 | value = info.value;
|
244 | } catch (error) {
|
245 | reject(error);
|
246 | return;
|
247 | }
|
248 | if (info.done) {
|
249 | resolve(value);
|
250 | } else {
|
251 | return Promise.resolve(value).then(
|
252 | value => {
|
253 | step('next', value);
|
254 | },
|
255 | err => {
|
256 | step('throw', err);
|
257 | });
|
258 | }
|
259 | }
|
260 | return step('next');
|
261 | });
|
262 | })(function*(){
|
263 | let delay = 0;
|
264 | if (options && options.delay)
|
265 | delay = options.delay;
|
266 | for (const char of text) {
|
267 | if (keyDefinitions[char])
|
268 | (yield this.press(char, {delay}));
|
269 | else
|
270 | (yield this.sendCharacter(char));
|
271 | if (delay)
|
272 | (yield new Promise(f => setTimeout(f, delay)));
|
273 | }
|
274 | });}
|
275 |
|
276 | |
277 |
|
278 |
|
279 |
|
280 | press(key, options) {return (fn => {
|
281 | const gen = fn.call(this);
|
282 | return new Promise((resolve, reject) => {
|
283 | function step(key, arg) {
|
284 | let info, value;
|
285 | try {
|
286 | info = gen[key](arg);
|
287 | value = info.value;
|
288 | } catch (error) {
|
289 | reject(error);
|
290 | return;
|
291 | }
|
292 | if (info.done) {
|
293 | resolve(value);
|
294 | } else {
|
295 | return Promise.resolve(value).then(
|
296 | value => {
|
297 | step('next', value);
|
298 | },
|
299 | err => {
|
300 | step('throw', err);
|
301 | });
|
302 | }
|
303 | }
|
304 | return step('next');
|
305 | });
|
306 | })(function*(){
|
307 | (yield this.down(key, options));
|
308 | if (options && options.delay)
|
309 | (yield new Promise(f => setTimeout(f, options.delay)));
|
310 | (yield this.up(key));
|
311 | });}
|
312 | }
|
313 |
|
314 | class Mouse {
|
315 | |
316 |
|
317 |
|
318 |
|
319 | constructor(client, keyboard) {
|
320 | this._client = client;
|
321 | this._keyboard = keyboard;
|
322 | this._x = 0;
|
323 | this._y = 0;
|
324 |
|
325 | this._button = 'none';
|
326 | }
|
327 |
|
328 | |
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 | move(x, y, options = {}) {return (fn => {
|
335 | const gen = fn.call(this);
|
336 | return new Promise((resolve, reject) => {
|
337 | function step(key, arg) {
|
338 | let info, value;
|
339 | try {
|
340 | info = gen[key](arg);
|
341 | value = info.value;
|
342 | } catch (error) {
|
343 | reject(error);
|
344 | return;
|
345 | }
|
346 | if (info.done) {
|
347 | resolve(value);
|
348 | } else {
|
349 | return Promise.resolve(value).then(
|
350 | value => {
|
351 | step('next', value);
|
352 | },
|
353 | err => {
|
354 | step('throw', err);
|
355 | });
|
356 | }
|
357 | }
|
358 | return step('next');
|
359 | });
|
360 | })(function*(){
|
361 | const fromX = this._x, fromY = this._y;
|
362 | this._x = x;
|
363 | this._y = y;
|
364 | const steps = options.steps || 1;
|
365 | for (let i = 1; i <= steps; i++) {
|
366 | (yield this._client.send('Input.dispatchMouseEvent', {
|
367 | type: 'mouseMoved',
|
368 | button: this._button,
|
369 | x: fromX + (this._x - fromX) * (i / steps),
|
370 | y: fromY + (this._y - fromY) * (i / steps),
|
371 | modifiers: this._keyboard._modifiers
|
372 | }));
|
373 | }
|
374 | });}
|
375 |
|
376 | |
377 |
|
378 |
|
379 |
|
380 |
|
381 | click(x, y, options = {}) {return (fn => {
|
382 | const gen = fn.call(this);
|
383 | return new Promise((resolve, reject) => {
|
384 | function step(key, arg) {
|
385 | let info, value;
|
386 | try {
|
387 | info = gen[key](arg);
|
388 | value = info.value;
|
389 | } catch (error) {
|
390 | reject(error);
|
391 | return;
|
392 | }
|
393 | if (info.done) {
|
394 | resolve(value);
|
395 | } else {
|
396 | return Promise.resolve(value).then(
|
397 | value => {
|
398 | step('next', value);
|
399 | },
|
400 | err => {
|
401 | step('throw', err);
|
402 | });
|
403 | }
|
404 | }
|
405 | return step('next');
|
406 | });
|
407 | })(function*(){
|
408 | this.move(x, y);
|
409 | this.down(options);
|
410 | if (typeof options.delay === 'number')
|
411 | (yield new Promise(f => setTimeout(f, options.delay)));
|
412 | (yield this.up(options));
|
413 | });}
|
414 |
|
415 | |
416 |
|
417 |
|
418 | down(options = {}) {return (fn => {
|
419 | const gen = fn.call(this);
|
420 | return new Promise((resolve, reject) => {
|
421 | function step(key, arg) {
|
422 | let info, value;
|
423 | try {
|
424 | info = gen[key](arg);
|
425 | value = info.value;
|
426 | } catch (error) {
|
427 | reject(error);
|
428 | return;
|
429 | }
|
430 | if (info.done) {
|
431 | resolve(value);
|
432 | } else {
|
433 | return Promise.resolve(value).then(
|
434 | value => {
|
435 | step('next', value);
|
436 | },
|
437 | err => {
|
438 | step('throw', err);
|
439 | });
|
440 | }
|
441 | }
|
442 | return step('next');
|
443 | });
|
444 | })(function*(){
|
445 | this._button = (options.button || 'left');
|
446 | (yield this._client.send('Input.dispatchMouseEvent', {
|
447 | type: 'mousePressed',
|
448 | button: this._button,
|
449 | x: this._x,
|
450 | y: this._y,
|
451 | modifiers: this._keyboard._modifiers,
|
452 | clickCount: (options.clickCount || 1)
|
453 | }));
|
454 | });}
|
455 |
|
456 | |
457 |
|
458 |
|
459 | up(options = {}) {return (fn => {
|
460 | const gen = fn.call(this);
|
461 | return new Promise((resolve, reject) => {
|
462 | function step(key, arg) {
|
463 | let info, value;
|
464 | try {
|
465 | info = gen[key](arg);
|
466 | value = info.value;
|
467 | } catch (error) {
|
468 | reject(error);
|
469 | return;
|
470 | }
|
471 | if (info.done) {
|
472 | resolve(value);
|
473 | } else {
|
474 | return Promise.resolve(value).then(
|
475 | value => {
|
476 | step('next', value);
|
477 | },
|
478 | err => {
|
479 | step('throw', err);
|
480 | });
|
481 | }
|
482 | }
|
483 | return step('next');
|
484 | });
|
485 | })(function*(){
|
486 | this._button = 'none';
|
487 | (yield this._client.send('Input.dispatchMouseEvent', {
|
488 | type: 'mouseReleased',
|
489 | button: (options.button || 'left'),
|
490 | x: this._x,
|
491 | y: this._y,
|
492 | modifiers: this._keyboard._modifiers,
|
493 | clickCount: (options.clickCount || 1)
|
494 | }));
|
495 | });}
|
496 | }
|
497 |
|
498 | class Touchscreen {
|
499 | |
500 |
|
501 |
|
502 |
|
503 | constructor(client, keyboard) {
|
504 | this._client = client;
|
505 | this._keyboard = keyboard;
|
506 | }
|
507 |
|
508 | |
509 |
|
510 |
|
511 |
|
512 | tap(x, y) {return (fn => {
|
513 | const gen = fn.call(this);
|
514 | return new Promise((resolve, reject) => {
|
515 | function step(key, arg) {
|
516 | let info, value;
|
517 | try {
|
518 | info = gen[key](arg);
|
519 | value = info.value;
|
520 | } catch (error) {
|
521 | reject(error);
|
522 | return;
|
523 | }
|
524 | if (info.done) {
|
525 | resolve(value);
|
526 | } else {
|
527 | return Promise.resolve(value).then(
|
528 | value => {
|
529 | step('next', value);
|
530 | },
|
531 | err => {
|
532 | step('throw', err);
|
533 | });
|
534 | }
|
535 | }
|
536 | return step('next');
|
537 | });
|
538 | })(function*(){
|
539 |
|
540 |
|
541 |
|
542 | (yield this._client.send('Runtime.evaluate', {
|
543 | expression: 'new Promise(x => requestAnimationFrame(() => requestAnimationFrame(x)))',
|
544 | awaitPromise: true
|
545 | }));
|
546 |
|
547 | const touchPoints = [{x: Math.round(x), y: Math.round(y)}];
|
548 | (yield this._client.send('Input.dispatchTouchEvent', {
|
549 | type: 'touchStart',
|
550 | touchPoints,
|
551 | modifiers: this._keyboard._modifiers
|
552 | }));
|
553 | (yield this._client.send('Input.dispatchTouchEvent', {
|
554 | type: 'touchEnd',
|
555 | touchPoints: [],
|
556 | modifiers: this._keyboard._modifiers
|
557 | }));
|
558 | });}
|
559 | }
|
560 |
|
561 | module.exports = { Keyboard, Mouse, Touchscreen};
|
562 | helper.tracePublicAPI(Keyboard);
|
563 | helper.tracePublicAPI(Mouse);
|
564 | helper.tracePublicAPI(Touchscreen);
|