Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/puppet-chrome/lib/Keyboard.ts
1028 views
1
/**
2
* Copyright 2018 Google Inc. All rights reserved.
3
* Modifications copyright (c) Data Liberation Foundation Inc.
4
*
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
* you may not use this file except in compliance with the License.
7
* You may obtain a copy of the License at
8
*
9
* http://www.apache.org/licenses/LICENSE-2.0
10
*
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
16
*/
17
import { IKeyboardKey } from '@secret-agent/interfaces/IKeyboardLayoutUS';
18
import { assert } from '@secret-agent/commons/utils';
19
import { IPuppetKeyboard } from '@secret-agent/interfaces/IPuppetInput';
20
import { IKeyDefinition, keyDefinitions } from '../interfaces/USKeyboardLayout';
21
import { DevtoolsSession } from './DevtoolsSession';
22
23
type KeyDescription = Required<Pick<IKeyDefinition, 'key' | 'text' | 'code' | 'location'>> & {
24
keyCode: number;
25
};
26
27
export class Keyboard implements IPuppetKeyboard {
28
public modifiers = 0;
29
private devtoolsSession: DevtoolsSession;
30
private pressedKeys = new Set<string>();
31
32
constructor(devtoolsSession: DevtoolsSession) {
33
this.devtoolsSession = devtoolsSession;
34
}
35
36
async down(key: IKeyboardKey): Promise<void> {
37
const description = this.keyDescriptionForString(key);
38
39
const autoRepeat = this.pressedKeys.has(description.code);
40
this.pressedKeys.add(description.code);
41
this.modifiers |= Keyboard.modifierBit(description.key);
42
43
const text = description.text;
44
await this.devtoolsSession.send('Input.dispatchKeyEvent', {
45
type: text ? 'keyDown' : 'rawKeyDown',
46
modifiers: this.modifiers,
47
windowsVirtualKeyCode: description.keyCode,
48
code: description.code,
49
key: description.key,
50
text,
51
unmodifiedText: text,
52
autoRepeat,
53
location: description.location,
54
isKeypad: description.location === 3,
55
});
56
}
57
58
async up(key: IKeyboardKey): Promise<void> {
59
const description = this.keyDescriptionForString(key);
60
61
this.modifiers &= ~Keyboard.modifierBit(description.key);
62
this.pressedKeys.delete(description.code);
63
await this.devtoolsSession.send('Input.dispatchKeyEvent', {
64
type: 'keyUp',
65
modifiers: this.modifiers,
66
key: description.key,
67
windowsVirtualKeyCode: description.keyCode,
68
code: description.code,
69
location: description.location,
70
});
71
}
72
73
async sendCharacter(char: string): Promise<void> {
74
await this.devtoolsSession.send('Input.insertText', { text: char });
75
}
76
77
async press(key: IKeyboardKey, keyupDelay?: number): Promise<void> {
78
await this.down(key);
79
if (keyupDelay) await new Promise(resolve => setTimeout(resolve, keyupDelay));
80
await this.up(key);
81
}
82
83
private keyDescriptionForString(keyString: IKeyboardKey): KeyDescription {
84
const shift = this.modifiers & 8;
85
const description = {
86
key: '',
87
keyCode: 0,
88
code: '',
89
text: '',
90
location: 0,
91
};
92
93
const definition = keyDefinitions[keyString];
94
assert(definition, `Unknown key: "${keyString}"`);
95
96
if (definition.key) description.key = definition.key;
97
if (shift && definition.shiftKey) description.key = definition.shiftKey;
98
99
if (definition.keyCode) description.keyCode = definition.keyCode;
100
if (shift && definition.shiftKeyCode) description.keyCode = definition.shiftKeyCode;
101
102
if (definition.code) description.code = definition.code;
103
104
if (definition.location) description.location = definition.location;
105
106
if (description.key.length === 1) description.text = description.key;
107
108
if (definition.text) description.text = definition.text;
109
if (shift && definition.shiftText) description.text = definition.shiftText;
110
111
// if any modifiers besides shift are pressed, no text should be sent
112
if (this.modifiers & ~8) description.text = '';
113
114
return description;
115
}
116
117
private static modifierBit(key: string): number {
118
if (key === 'Alt') return 1;
119
if (key === 'Control') return 2;
120
if (key === 'Meta') return 4;
121
if (key === 'Shift') return 8;
122
return 0;
123
}
124
}
125
126