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

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

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

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

	_serviceBrand: any;

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

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

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

J
Joao Moreno 已提交
47
	reloadWindowDriver(windowId: number): TPromise<void> {
J
Joao Moreno 已提交
48
		this.reloadingWindowIds.add(windowId);
M
Matt Bierner 已提交
49
		return TPromise.as(void 0);
J
Joao Moreno 已提交
50 51
	}

J
Joao Moreno 已提交
52 53
	getWindowIds(): TPromise<number[]> {
		return TPromise.as(this.windowsService.getWindows()
J
Joao Moreno 已提交
54
			.map(w => w.id)
J
Joao Moreno 已提交
55
			.filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id)));
J
Joao Moreno 已提交
56 57
	}

J
Joao Moreno 已提交
58 59 60 61 62 63
	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 已提交
64 65
	}

J
Joao Moreno 已提交
66 67 68 69 70 71
	reloadWindow(windowId: number): TPromise<void> {
		return this.whenUnfrozen(windowId).then(() => {
			const window = this.windowsService.getWindowById(windowId);
			this.reloadingWindowIds.add(windowId);
			this.windowsService.reload(window);
		});
72 73
	}

J
Joao Moreno 已提交
74 75
	dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> {
		return this.whenUnfrozen(windowId).then(() => {
J
Joao Moreno 已提交
76
			const [first, second] = KeybindingParser.parseUserBinding(keybinding);
M
Matt Bierner 已提交
77 78 79
			if (!first) {
				return undefined;
			}
J
Joao Moreno 已提交
80 81 82 83
			return this._dispatchKeybinding(windowId, first).then(() => {
				if (second) {
					return this._dispatchKeybinding(windowId, second);
				} else {
M
Matt Bierner 已提交
84
					return TPromise.as(void 0);
J
Joao Moreno 已提交
85 86 87
				}
			});
		});
J
Joao Moreno 已提交
88 89
	}

J
Joao Moreno 已提交
90
	private _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): TPromise<void> {
J
Joao Moreno 已提交
91
		if (keybinding instanceof ScanCodeBinding) {
J
Joao Moreno 已提交
92
			return TPromise.wrapError(new Error('ScanCodeBindings not supported'));
J
Joao Moreno 已提交
93 94 95 96 97 98 99 100
		}

		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();

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

		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 已提交
120 121 122 123 124

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

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

J
Joao Moreno 已提交
127
		return TPromise.wrap(timeout(100));
J
Joao Moreno 已提交
128 129
	}

J
Joao Moreno 已提交
130 131 132 133
	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 已提交
134 135
	}

J
Joao Moreno 已提交
136 137 138 139
	doubleClick(windowId: number, selector: string): TPromise<void> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.doubleClick(selector);
		});
J
Joao Moreno 已提交
140 141
	}

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

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

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

J
Joao Moreno 已提交
160 161 162 163
	getElements(windowId: number, selector: string, recursive: boolean): TPromise<IElement[]> {
		return this.getWindowDriver(windowId).then(windowDriver => {
			return windowDriver.getElements(selector, recursive);
		});
J
Joao Moreno 已提交
164 165
	}

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

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

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

J
Joao Moreno 已提交
184 185
	private getWindowDriver(windowId: number): TPromise<IWindowDriver> {
		return this.whenUnfrozen(windowId).then(() => {
J
Joao Moreno 已提交
186 187 188
			const id = `window:${windowId}`;
			const router = new StaticRouter(ctx => ctx === id);
			const windowDriverChannel = this.windowServer.getChannel('windowDriver', router);
J
Joao Moreno 已提交
189 190 191
			return new WindowDriverChannelClient(windowDriverChannel);
		});
	}
J
Joao Moreno 已提交
192

J
Joao Moreno 已提交
193
	private whenUnfrozen(windowId: number): TPromise<void> {
194
		return TPromise.wrap(this._whenUnfrozen(windowId));
J
Joao Moreno 已提交
195
	}
J
Joao Moreno 已提交
196

J
Joao Moreno 已提交
197
	private async _whenUnfrozen(windowId: number): Promise<void> {
J
Joao Moreno 已提交
198 199 200 201
		while (this.reloadingWindowIds.has(windowId)) {
			await toPromise(this.onDidReloadingChange.event);
		}
	}
J
Joao Moreno 已提交
202 203 204 205 206
}

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

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