driver.ts 7.4 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';
J
Joao Moreno 已提交
9
import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver } 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';
J
Joao Moreno 已提交
19 20 21 22

// 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 已提交
23
import { NativeImage } from 'electron';
J
Joao Moreno 已提交
24 25 26 27 28 29 30 31 32 33

class WindowRouter implements IClientRouter {

	constructor(private windowId: number) { }

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

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

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

	_serviceBrand: any;

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

	constructor(
		private windowServer: IPCServer,
		@IWindowsMainService private windowsService: IWindowsMainService
	) { }

J
Joao Moreno 已提交
51
	async registerWindowDriver(windowId: number): TPromise<void> {
J
Joao Moreno 已提交
52
		this.registeredWindowIds.add(windowId);
J
Joao Moreno 已提交
53 54 55 56 57 58
		this.reloadingWindowIds.delete(windowId);
		this.onDidReloadingChange.fire();
	}

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

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

J
Joao Moreno 已提交
67 68 69 70 71 72 73 74 75 76 77
	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');
	}

78 79 80 81 82
	async reloadWindow(windowId: number): TPromise<void> {
		await this.whenUnfrozen(windowId);

		const window = this.windowsService.getWindowById(windowId);
		this.reloadingWindowIds.add(windowId);
J
Joao Moreno 已提交
83
		this.windowsService.reload(window);
84 85
	}

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

J
Joao Moreno 已提交
89 90 91 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
		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 已提交
128 129 130 131 132

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

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

		await TPromise.timeout(100);
J
Joao Moreno 已提交
136 137
	}

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

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

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

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

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

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

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

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

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

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

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

	private async whenUnfrozen(windowId: number): TPromise<void> {
		while (this.reloadingWindowIds.has(windowId)) {
			await toPromise(this.onDidReloadingChange.event);
		}
	}
J
Joao Moreno 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
}

export async function serve(
	windowServer: IPCServer,
	handle: string,
	instantiationService: IInstantiationService
): TPromise<IDisposable> {
	const driver = instantiationService.createInstance(Driver, windowServer);

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