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

'use strict';

import { TPromise } from 'vs/base/common/winjs.base';
9
import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/common/driver';
J
Joao Moreno 已提交
10 11 12 13 14
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';
import { IPCServer, IClientRouter } from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
15
import { SimpleKeybinding, KeyCode } from 'vs/base/common/keyCodes';
J
Joao Moreno 已提交
16 17
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
import { OS } from 'vs/base/common/platform';
J
Joao Moreno 已提交
18
import { Emitter, toPromise } from 'vs/base/common/event';
19
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
J
Joao Moreno 已提交
20 21 22 23

// TODO@joao: bad layering!
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
import { ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode';
J
Joao Moreno 已提交
24
import { NativeImage } from 'electron';
J
Joao Moreno 已提交
25 26 27 28 29 30 31 32 33 34

class WindowRouter implements IClientRouter {

	constructor(private windowId: number) { }

	route(command: string, arg: any): string {
		return `window:${this.windowId}`;
	}
}

J
Joao Moreno 已提交
35 36 37 38
function isSilentKeyCode(keyCode: KeyCode) {
	return keyCode < KeyCode.KEY_0;
}

J
Joao Moreno 已提交
39 40 41 42 43
export class Driver implements IDriver, IWindowDriverRegistry {

	_serviceBrand: any;

	private registeredWindowIds = new Set<number>();
J
Joao Moreno 已提交
44 45
	private reloadingWindowIds = new Set<number>();
	private onDidReloadingChange = new Emitter<void>();
J
Joao Moreno 已提交
46 47 48

	constructor(
		private windowServer: IPCServer,
49
		private options: IDriverOptions,
J
Joao Moreno 已提交
50 51 52
		@IWindowsMainService private windowsService: IWindowsMainService
	) { }

53
	async registerWindowDriver(windowId: number): TPromise<IDriverOptions> {
J
Joao Moreno 已提交
54
		this.registeredWindowIds.add(windowId);
J
Joao Moreno 已提交
55 56
		this.reloadingWindowIds.delete(windowId);
		this.onDidReloadingChange.fire();
57
		return this.options;
J
Joao Moreno 已提交
58 59 60 61
	}

	async reloadWindowDriver(windowId: number): TPromise<void> {
		this.reloadingWindowIds.add(windowId);
J
Joao Moreno 已提交
62 63 64 65 66
	}

	async getWindowIds(): TPromise<number[]> {
		return this.windowsService.getWindows()
			.map(w => w.id)
J
Joao Moreno 已提交
67
			.filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id));
J
Joao Moreno 已提交
68 69
	}

J
Joao Moreno 已提交
70 71 72 73 74 75 76 77 78 79 80
	async capturePage(windowId: number): TPromise<string> {
		await this.whenUnfrozen(windowId);

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

		return buffer.toString('base64');
	}

81 82 83 84 85
	async reloadWindow(windowId: number): TPromise<void> {
		await this.whenUnfrozen(windowId);

		const window = this.windowsService.getWindowById(windowId);
		this.reloadingWindowIds.add(windowId);
J
Joao Moreno 已提交
86
		this.windowsService.reload(window);
87 88
	}

J
Joao Moreno 已提交
89
	async dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> {
J
Joao Moreno 已提交
90 91
		await this.whenUnfrozen(windowId);

J
Joao Moreno 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
		const [first, second] = KeybindingIO._readUserBinding(keybinding);

		await this._dispatchKeybinding(windowId, first);

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

	private async _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): TPromise<void> {
		if (keybinding instanceof ScanCodeBinding) {
			throw new Error('ScanCodeBindings not supported');
		}

		const window = this.windowsService.getWindowById(windowId);
		const webContents = window.win.webContents;
		const noModifiedKeybinding = new SimpleKeybinding(false, false, false, false, keybinding.keyCode);
		const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding, OS);
		const keyCode = resolvedKeybinding.getElectronAccelerator();

		const modifiers = [];

		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 已提交
131 132 133 134 135

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

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

		await TPromise.timeout(100);
J
Joao Moreno 已提交
139 140
	}

J
Joao Moreno 已提交
141 142
	async click(windowId: number, selector: string, xoffset?: number, yoffset?: number): TPromise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
143 144 145
		return windowDriver.click(selector, xoffset, yoffset);
	}

J
Joao Moreno 已提交
146 147
	async doubleClick(windowId: number, selector: string): TPromise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
148 149 150
		return windowDriver.doubleClick(selector);
	}

J
Joao Moreno 已提交
151 152
	async setValue(windowId: number, selector: string, text: string): TPromise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
153
		return windowDriver.setValue(selector, text);
J
Joao Moreno 已提交
154 155
	}

J
Joao Moreno 已提交
156 157
	async getTitle(windowId: number): TPromise<string> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
158 159 160
		return windowDriver.getTitle();
	}

J
Joao Moreno 已提交
161 162
	async isActiveElement(windowId: number, selector: string): TPromise<boolean> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
163 164 165
		return windowDriver.isActiveElement(selector);
	}

J
Joao Moreno 已提交
166 167
	async getElements(windowId: number, selector: string, recursive: boolean): TPromise<IElement[]> {
		const windowDriver = await this.getWindowDriver(windowId);
168
		return windowDriver.getElements(selector, recursive);
J
Joao Moreno 已提交
169 170
	}

J
Joao Moreno 已提交
171 172
	async typeInEditor(windowId: number, selector: string, text: string): TPromise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
173
		return windowDriver.typeInEditor(selector, text);
J
Joao Moreno 已提交
174 175
	}

J
Joao Moreno 已提交
176 177
	async getTerminalBuffer(windowId: number, selector: string): TPromise<string[]> {
		const windowDriver = await this.getWindowDriver(windowId);
J
Joao Moreno 已提交
178
		return windowDriver.getTerminalBuffer(selector);
J
Joao Moreno 已提交
179 180
	}

J
Joao Moreno 已提交
181 182 183 184 185
	async writeInTerminal(windowId: number, selector: string, text: string): TPromise<void> {
		const windowDriver = await this.getWindowDriver(windowId);
		return windowDriver.writeInTerminal(selector, text);
	}

J
Joao Moreno 已提交
186 187 188
	private async getWindowDriver(windowId: number): TPromise<IWindowDriver> {
		await this.whenUnfrozen(windowId);

J
Joao Moreno 已提交
189 190
		const router = new WindowRouter(windowId);
		const windowDriverChannel = this.windowServer.getChannel<IWindowDriverChannel>('windowDriver', router);
J
Joao Moreno 已提交
191
		return new WindowDriverChannelClient(windowDriverChannel);
J
Joao Moreno 已提交
192
	}
J
Joao Moreno 已提交
193 194 195 196 197 198

	private async whenUnfrozen(windowId: number): TPromise<void> {
		while (this.reloadingWindowIds.has(windowId)) {
			await toPromise(this.onDidReloadingChange.event);
		}
	}
J
Joao Moreno 已提交
199 200 201 202 203
}

export async function serve(
	windowServer: IPCServer,
	handle: string,
204
	environmentService: IEnvironmentService,
J
Joao Moreno 已提交
205 206
	instantiationService: IInstantiationService
): TPromise<IDisposable> {
207 208
	const verbose = environmentService.driverVerbose;
	const driver = instantiationService.createInstance(Driver, windowServer, { verbose });
J
Joao Moreno 已提交
209 210 211 212 213 214 215 216 217 218

	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]);
}