driver.ts 7.5 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6
import { IDriver, DriverChannel, IElement, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/node/driver';
J
Joao Moreno 已提交
7 8 9 10
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net';
import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
J
Joao Moreno 已提交
11
import { IPCServer, StaticRouter } from 'vs/base/parts/ipc/node/ipc';
J
Joao Moreno 已提交
12
import { SimpleKeybinding, KeyCode } from 'vs/base/common/keyCodes';
J
Joao Moreno 已提交
13 14
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { OS } from 'vs/base/common/platform';
J
Joao Moreno 已提交
15
import { Emitter, Event } from 'vs/base/common/event';
16
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
J
Joao Moreno 已提交
17 18
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
J
Joao Moreno 已提交
19
import { timeout } from 'vs/base/common/async';
J
Joao Moreno 已提交
20

J
Joao Moreno 已提交
21 22 23 24
function isSilentKeyCode(keyCode: KeyCode) {
	return keyCode < KeyCode.KEY_0;
}

J
Joao Moreno 已提交
25 26 27 28 29
export class Driver implements IDriver, IWindowDriverRegistry {

	_serviceBrand: any;

	private registeredWindowIds = new Set<number>();
J
Joao Moreno 已提交
30 31
	private reloadingWindowIds = new Set<number>();
	private onDidReloadingChange = new Emitter<void>();
J
Joao Moreno 已提交
32 33 34

	constructor(
		private windowServer: IPCServer,
35
		private options: IDriverOptions,
36
		@IWindowsMainService private readonly windowsService: IWindowsMainService
J
Joao Moreno 已提交
37 38
	) { }

J
Joao Moreno 已提交
39
	async registerWindowDriver(windowId: number): Promise<IDriverOptions> {
J
Joao Moreno 已提交
40
		this.registeredWindowIds.add(windowId);
J
Joao Moreno 已提交
41 42
		this.reloadingWindowIds.delete(windowId);
		this.onDidReloadingChange.fire();
J
Joao Moreno 已提交
43
		return this.options;
J
Joao Moreno 已提交
44 45
	}

J
Joao Moreno 已提交
46
	async reloadWindowDriver(windowId: number): Promise<void> {
J
Joao Moreno 已提交
47
		this.reloadingWindowIds.add(windowId);
J
Joao Moreno 已提交
48 49
	}

J
Joao Moreno 已提交
50 51
	async getWindowIds(): Promise<number[]> {
		return this.windowsService.getWindows()
J
Joao Moreno 已提交
52
			.map(w => w.id)
J
Joao Moreno 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
			.filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id));
	}

	async capturePage(windowId: number): Promise<string> {
		await this.whenUnfrozen(windowId);

		const window = this.windowsService.getWindowById(windowId);
		const webContents = window.win.webContents;
		const image = await new Promise<Electron.NativeImage>(c => webContents.capturePage(c));

		return image.toPNG().toString('base64');
	}

	async reloadWindow(windowId: number): Promise<void> {
		await this.whenUnfrozen(windowId);

		const window = this.windowsService.getWindowById(windowId);
		this.reloadingWindowIds.add(windowId);
		this.windowsService.reload(window);
	}

K
Kai Maetzel 已提交
74 75 76 77
	async exitApplication(): Promise<void> {
		return this.windowsService.quit();
	}

J
Joao Moreno 已提交
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
	async dispatchKeybinding(windowId: number, keybinding: string): Promise<void> {
		await this.whenUnfrozen(windowId);

		const [first, second] = KeybindingParser.parseUserBinding(keybinding);

		if (!first) {
			return;
		}

		await this._dispatchKeybinding(windowId, first);

		if (second) {
			await this._dispatchKeybinding(windowId, second);
		}
	}

	private async _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): Promise<void> {
J
Joao Moreno 已提交
95
		if (keybinding instanceof ScanCodeBinding) {
J
Joao Moreno 已提交
96
			throw new Error('ScanCodeBindings not supported');
J
Joao Moreno 已提交
97 98 99 100 101
		}

		const window = this.windowsService.getWindowById(windowId);
		const webContents = window.win.webContents;
		const noModifiedKeybinding = new SimpleKeybinding(false, false, false, false, keybinding.keyCode);
102
		const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding.toChord(), OS);
J
Joao Moreno 已提交
103 104
		const keyCode = resolvedKeybinding.getElectronAccelerator();

M
Matt Bierner 已提交
105
		const modifiers: string[] = [];
J
Joao Moreno 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

		if (keybinding.ctrlKey) {
			modifiers.push('ctrl');
		}

		if (keybinding.metaKey) {
			modifiers.push('meta');
		}

		if (keybinding.shiftKey) {
			modifiers.push('shift');
		}

		if (keybinding.altKey) {
			modifiers.push('alt');
		}

		webContents.sendInputEvent({ type: 'keyDown', keyCode, modifiers } as any);
J
Joao Moreno 已提交
124 125 126 127 128

		if (!isSilentKeyCode(keybinding.keyCode)) {
			webContents.sendInputEvent({ type: 'char', keyCode, modifiers } as any);
		}

J
Joao Moreno 已提交
129 130
		webContents.sendInputEvent({ type: 'keyUp', keyCode, modifiers } as any);

J
Joao Moreno 已提交
131
		await timeout(100);
J
Joao Moreno 已提交
132 133
	}

J
Joao Moreno 已提交
134 135 136
	async click(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
		await windowDriver.click(selector, xoffset, yoffset);
J
Joao Moreno 已提交
137 138
	}

J
Joao Moreno 已提交
139 140 141
	async doubleClick(windowId: number, selector: string): Promise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
		await windowDriver.doubleClick(selector);
J
Joao Moreno 已提交
142 143
	}

J
Joao Moreno 已提交
144 145 146
	async setValue(windowId: number, selector: string, text: string): Promise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
		await windowDriver.setValue(selector, text);
J
Joao Moreno 已提交
147 148
	}

J
Joao Moreno 已提交
149 150 151
	async getTitle(windowId: number): Promise<string> {
		const windowDriver = await this.getWindowDriver(windowId);
		return await windowDriver.getTitle();
J
Joao Moreno 已提交
152 153
	}

J
Joao Moreno 已提交
154 155 156
	async isActiveElement(windowId: number, selector: string): Promise<boolean> {
		const windowDriver = await this.getWindowDriver(windowId);
		return await windowDriver.isActiveElement(selector);
J
Joao Moreno 已提交
157 158
	}

J
Joao Moreno 已提交
159 160 161
	async getElements(windowId: number, selector: string, recursive: boolean): Promise<IElement[]> {
		const windowDriver = await this.getWindowDriver(windowId);
		return await windowDriver.getElements(selector, recursive);
J
Joao Moreno 已提交
162 163
	}

J
Joao Moreno 已提交
164 165 166
	async typeInEditor(windowId: number, selector: string, text: string): Promise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
		await windowDriver.typeInEditor(selector, text);
J
Joao Moreno 已提交
167 168
	}

J
Joao Moreno 已提交
169 170 171
	async getTerminalBuffer(windowId: number, selector: string): Promise<string[]> {
		const windowDriver = await this.getWindowDriver(windowId);
		return await windowDriver.getTerminalBuffer(selector);
J
Joao Moreno 已提交
172 173
	}

J
Joao Moreno 已提交
174 175 176
	async writeInTerminal(windowId: number, selector: string, text: string): Promise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
		await windowDriver.writeInTerminal(selector, text);
J
Joao Moreno 已提交
177 178
	}

J
Joao Moreno 已提交
179 180
	private async getWindowDriver(windowId: number): Promise<IWindowDriver> {
		await this.whenUnfrozen(windowId);
J
Joao Moreno 已提交
181

J
Joao Moreno 已提交
182 183 184 185
		const id = `window:${windowId}`;
		const router = new StaticRouter(ctx => ctx === id);
		const windowDriverChannel = this.windowServer.getChannel('windowDriver', router);
		return new WindowDriverChannelClient(windowDriverChannel);
J
Joao Moreno 已提交
186
	}
J
Joao Moreno 已提交
187

J
Joao Moreno 已提交
188
	private async whenUnfrozen(windowId: number): Promise<void> {
J
Joao Moreno 已提交
189
		while (this.reloadingWindowIds.has(windowId)) {
J
Joao Moreno 已提交
190
			await Event.toPromise(this.onDidReloadingChange.event);
J
Joao Moreno 已提交
191 192
		}
	}
J
Joao Moreno 已提交
193 194 195 196 197
}

export async function serve(
	windowServer: IPCServer,
	handle: string,
198
	environmentService: IEnvironmentService,
J
Joao Moreno 已提交
199
	instantiationService: IInstantiationService
200
): Promise<IDisposable> {
201 202
	const verbose = environmentService.driverVerbose;
	const driver = instantiationService.createInstance(Driver, windowServer, { verbose });
J
Joao Moreno 已提交
203 204 205 206 207 208 209 210 211

	const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver);
	windowServer.registerChannel('windowDriverRegistry', windowDriverRegistryChannel);

	const server = await serveNet(handle);
	const channel = new DriverChannel(driver);
	server.registerChannel('driver', channel);

	return combinedDisposable([server, windowServer]);
212
}