driver.ts 8.0 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 { toWinJsPromise } from 'vs/base/common/async';
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
	) { }

J
Joao Moreno 已提交
53
	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();
J
Joao Moreno 已提交
57
		return TPromise.as(this.options);
J
Joao Moreno 已提交
58 59
	}

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

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

J
Joao Moreno 已提交
71 72 73 74 75 76
	capturePage(windowId: number): TPromise<string> {
		return this.whenUnfrozen(windowId).then(() => {
			const window = this.windowsService.getWindowById(windowId);
			const webContents = window.win.webContents;
			return new TPromise(c => webContents.capturePage(image => c(image.toPNG().toString('base64'))));
		});
J
Joao Moreno 已提交
77 78
	}

J
Joao Moreno 已提交
79 80 81 82 83 84
	reloadWindow(windowId: number): TPromise<void> {
		return this.whenUnfrozen(windowId).then(() => {
			const window = this.windowsService.getWindowById(windowId);
			this.reloadingWindowIds.add(windowId);
			this.windowsService.reload(window);
		});
85 86
	}

J
Joao Moreno 已提交
87 88 89
	dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> {
		return this.whenUnfrozen(windowId).then(() => {
			const [first, second] = KeybindingIO._readUserBinding(keybinding);
J
Joao Moreno 已提交
90

J
Joao Moreno 已提交
91 92 93 94 95 96 97 98
			return this._dispatchKeybinding(windowId, first).then(() => {
				if (second) {
					return this._dispatchKeybinding(windowId, second);
				} else {
					return TPromise.as(null);
				}
			});
		});
J
Joao Moreno 已提交
99 100
	}

J
Joao Moreno 已提交
101
	private _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): TPromise<void> {
J
Joao Moreno 已提交
102
		if (keybinding instanceof ScanCodeBinding) {
J
Joao Moreno 已提交
103
			return TPromise.wrapError(new Error('ScanCodeBindings not supported'));
J
Joao Moreno 已提交
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 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
		webContents.sendInputEvent({ type: 'keyUp', keyCode, modifiers } as any);

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

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

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

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

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

J
Joao Moreno 已提交
165 166 167 168
	isActiveElement(windowId: number, selector: string): TPromise<boolean> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.isActiveElement(selector);
		});
J
Joao Moreno 已提交
169 170
	}

J
Joao Moreno 已提交
171 172 173 174
	getElements(windowId: number, selector: string, recursive: boolean): TPromise<IElement[]> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.getElements(selector, recursive);
		});
J
Joao Moreno 已提交
175 176
	}

J
Joao Moreno 已提交
177 178 179 180
	typeInEditor(windowId: number, selector: string, text: string): TPromise<void> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.typeInEditor(selector, text);
		});
J
Joao Moreno 已提交
181 182
	}

J
Joao Moreno 已提交
183 184 185 186
	getTerminalBuffer(windowId: number, selector: string): TPromise<string[]> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.getTerminalBuffer(selector);
		});
J
Joao Moreno 已提交
187 188
	}

J
Joao Moreno 已提交
189 190 191 192
	writeInTerminal(windowId: number, selector: string, text: string): TPromise<void> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.writeInTerminal(selector, text);
		});
J
Joao Moreno 已提交
193 194
	}

J
Joao Moreno 已提交
195 196 197 198 199 200 201
	private getWindowDriver(windowId: number): TPromise<IWindowDriver> {
		return this.whenUnfrozen(windowId).then(() => {
			const router = new WindowRouter(windowId);
			const windowDriverChannel = this.windowServer.getChannel<IWindowDriverChannel>('windowDriver', router);
			return new WindowDriverChannelClient(windowDriverChannel);
		});
	}
J
Joao Moreno 已提交
202

J
Joao Moreno 已提交
203 204
	private whenUnfrozen(windowId: number): TPromise<void> {
		return toWinJsPromise(this._whenUnfrozen(windowId));
J
Joao Moreno 已提交
205
	}
J
Joao Moreno 已提交
206

J
Joao Moreno 已提交
207
	private async _whenUnfrozen(windowId: number): Promise<void> {
J
Joao Moreno 已提交
208 209 210 211
		while (this.reloadingWindowIds.has(windowId)) {
			await toPromise(this.onDidReloadingChange.event);
		}
	}
J
Joao Moreno 已提交
212 213 214 215 216
}

export async function serve(
	windowServer: IPCServer,
	handle: string,
217
	environmentService: IEnvironmentService,
J
Joao Moreno 已提交
218 219
	instantiationService: IInstantiationService
): TPromise<IDisposable> {
220 221
	const verbose = environmentService.driverVerbose;
	const driver = instantiationService.createInstance(Driver, windowServer, { verbose });
J
Joao Moreno 已提交
222 223 224 225 226 227 228 229 230 231

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