app.ts 25.5 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

B
Benjamin Pasero 已提交
8
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing } from 'electron';
9
import * as platform from 'vs/base/common/platform';
10
import { WindowsManager } from 'vs/code/electron-main/windows';
J
Joao Moreno 已提交
11
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
J
Joao Moreno 已提交
12
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
13
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
14
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
B
Benjamin Pasero 已提交
15
import { getShellEnvironment } from 'vs/code/node/shellEnv';
16
import { IUpdateService } from 'vs/platform/update/common/update';
J
Joao Moreno 已提交
17
import { UpdateChannel } from 'vs/platform/update/node/updateIpc';
18 19 20 21
import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main';
import { Server, connect, Client } from 'vs/base/parts/ipc/node/ipc.net';
import { SharedProcess } from 'vs/code/electron-main/sharedProcess';
import { Mutex } from 'windows-mutex';
B
Benjamin Pasero 已提交
22
import { LaunchService, LaunchChannel, ILaunchService } from 'vs/platform/launch/electron-main/launchService';
23 24 25
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
26
import { ILogService } from 'vs/platform/log/common/log';
B
Benjamin Pasero 已提交
27
import { IStateService } from 'vs/platform/state/common/state';
28 29 30
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IURLService } from 'vs/platform/url/common/url';
J
Joao Moreno 已提交
31
import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/node/urlIpc';
32
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
33
import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils';
J
Joao Moreno 已提交
34
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
35
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
36
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
J
Joao Moreno 已提交
37
import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc';
38 39
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
40
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
B
Benjamin Pasero 已提交
41 42
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
43
import { TPromise } from 'vs/base/common/winjs.base';
B
Benjamin Pasero 已提交
44
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
B
Benjamin Pasero 已提交
45
import { IHistoryMainService } from 'vs/platform/history/common/history';
B
Benjamin Pasero 已提交
46
import { isUndefinedOrNull } from 'vs/base/common/types';
47
import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard';
48
import { URI } from 'vs/base/common/uri';
J
Joao Moreno 已提交
49
import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc';
B
Benjamin Pasero 已提交
50
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
51
import { getMachineId } from 'vs/base/node/id';
52 53 54
import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32';
import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux';
import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin';
55
import { IIssueService } from 'vs/platform/issue/common/issue';
J
Joao Moreno 已提交
56
import { IssueChannel } from 'vs/platform/issue/node/issueIpc';
P
Pine Wu 已提交
57
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
J
Joao Moreno 已提交
58
import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc';
A
Alex Dima 已提交
59
import * as errors from 'vs/base/common/errors';
J
Joao Moreno 已提交
60
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
J
Joao Moreno 已提交
61
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
62 63
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
J
Joao Moreno 已提交
64
import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc';
65
import { ILabelService, RegisterFormatterEvent } from 'vs/platform/label/common/label';
66
import { CodeMenu } from 'vs/code/electron-main/menus';
M
Martin Aeschlimann 已提交
67
import { hasArgs } from 'vs/platform/environment/node/argv';
68
import { RunOnceScheduler } from 'vs/base/common/async';
69
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
70
import { THEME_STORAGE_KEY, THEME_BG_STORAGE_KEY } from 'vs/code/electron-main/theme';
B
Benjamin Pasero 已提交
71 72 73
import { nativeSep, join } from 'vs/base/common/paths';
import { homedir } from 'os';
import { localize } from 'vs/nls';
74

B
Benjamin Pasero 已提交
75
export class CodeApplication {
B
Benjamin Pasero 已提交
76

77
	private static readonly MACHINE_ID_KEY = 'telemetry.machineId';
B
Benjamin Pasero 已提交
78

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	private toDispose: IDisposable[];
	private windowsMainService: IWindowsMainService;

	private electronIpcServer: ElectronIPCServer;

	private sharedProcess: SharedProcess;
	private sharedProcessClient: TPromise<Client>;

	constructor(
		private mainIpcServer: Server,
		private userEnv: platform.IProcessEnvironment,
		@IInstantiationService private instantiationService: IInstantiationService,
		@ILogService private logService: ILogService,
		@IEnvironmentService private environmentService: IEnvironmentService,
		@ILifecycleService private lifecycleService: ILifecycleService,
94
		@IConfigurationService private configurationService: ConfigurationService,
B
Benjamin Pasero 已提交
95
		@IStateService private stateService: IStateService,
96
		@IHistoryMainService private historyMainService: IHistoryMainService,
I
isidor 已提交
97
		@ILabelService private labelService: ILabelService
98 99 100 101 102 103 104 105 106
	) {
		this.toDispose = [mainIpcServer, configurationService];

		this.registerListeners();
	}

	private registerListeners(): void {

		// We handle uncaught exceptions here to prevent electron from opening a dialog to the user
A
Alex Dima 已提交
107
		errors.setUnexpectedErrorHandler(err => this.onUnexpectedError(err));
B
Benjamin Pasero 已提交
108
		process.on('uncaughtException', err => this.onUnexpectedError(err));
109
		process.on('unhandledRejection', (reason: any, promise: Promise<any>) => errors.onUnexpectedError(reason));
110

111 112 113
		// Contextmenu via IPC support
		registerContextMenuListener();

114
		app.on('will-quit', () => {
J
Joao Moreno 已提交
115
			this.logService.trace('App#will-quit: disposing resources');
116 117 118 119

			this.dispose();
		});

120 121 122 123 124 125 126
		app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => {
			if (this.windowsMainService) {
				this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
			}
		});

		app.on('activate', (event: Event, hasVisibleWindows: boolean) => {
J
Joao Moreno 已提交
127
			this.logService.trace('App#activate');
128 129 130 131 132 133 134

			// Mac only event: open new window when we get activated
			if (!hasVisibleWindows && this.windowsMainService) {
				this.windowsMainService.openNewWindow(OpenContext.DOCK);
			}
		});

135 136
		// Security related measures (https://electronjs.org/docs/tutorial/security)
		// DO NOT CHANGE without consulting the documentation
137
		app.on('web-contents-created', (event: any, contents) => {
138
			contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
139 140

				// Ensure defaults
M
Matt Bierner 已提交
141 142 143 144
				delete webPreferences.preload;
				webPreferences.nodeIntegration = false;

				// Verify URLs being loaded
145
				if (this.isValidWebviewSource(params.src) && this.isValidWebviewSource(webPreferences.preloadURL)) {
M
Matt Bierner 已提交
146 147
					return;
				}
M
Matt Bierner 已提交
148

149 150
				delete webPreferences.preloadUrl;

M
Matt Bierner 已提交
151
				// Otherwise prevent loading
B
Benjamin Pasero 已提交
152
				this.logService.error('webContents#web-contents-created: Prevented webview attach');
153

M
Matt Bierner 已提交
154 155 156 157
				event.preventDefault();
			});

			contents.on('will-navigate', event => {
B
Benjamin Pasero 已提交
158
				this.logService.error('webContents#will-navigate: Prevented webcontent navigation');
159

M
Matt Bierner 已提交
160 161
				event.preventDefault();
			});
162 163 164 165 166 167

			contents.on('new-window', (event: Event, url: string) => {
				event.preventDefault(); // prevent code that wants to open links

				shell.openExternal(url);
			});
M
Matt Bierner 已提交
168 169
		});

170
		let macOpenFileURIs: URI[] = [];
171 172
		let runningTimeout: number = null;
		app.on('open-file', (event: Event, path: string) => {
J
Joao Moreno 已提交
173
			this.logService.trace('App#open-file: ', path);
174 175 176
			event.preventDefault();

			// Keep in array because more might come!
177
			macOpenFileURIs.push(URI.file(path));
178 179 180 181 182 183 184 185 186 187 188 189 190

			// Clear previous handler if any
			if (runningTimeout !== null) {
				clearTimeout(runningTimeout);
				runningTimeout = null;
			}

			// Handle paths delayed in case more are coming!
			runningTimeout = setTimeout(() => {
				if (this.windowsMainService) {
					this.windowsMainService.open({
						context: OpenContext.DOCK /* can also be opening from finder while app is running */,
						cli: this.environmentService.args,
S
Sandeep Somavarapu 已提交
191
						urisToOpen: macOpenFileURIs,
192 193
						preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
					});
194
					macOpenFileURIs = [];
195 196 197 198 199
					runningTimeout = null;
				}
			}, 100);
		});

200 201 202 203
		app.on('new-window-for-tab', () => {
			this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
		});

204
		ipc.on('vscode:exit', (event: Event, code: number) => {
J
Joao Moreno 已提交
205
			this.logService.trace('IPC#vscode:exit', code);
206 207 208 209 210

			this.dispose();
			this.lifecycleService.kill(code);
		});

211 212
		ipc.on('vscode:fetchShellEnv', (event: Event) => {
			const webContents = event.sender;
213
			getShellEnvironment().then(shellEnv => {
J
Johannes Rieken 已提交
214 215 216
				if (!webContents.isDestroyed()) {
					webContents.send('vscode:acceptShellEnv', shellEnv);
				}
217
			}, err => {
J
Johannes Rieken 已提交
218 219 220
				if (!webContents.isDestroyed()) {
					webContents.send('vscode:acceptShellEnv', {});
				}
B
Benjamin Pasero 已提交
221 222

				this.logService.error('Error fetching shell env', err);
223 224
			});
		});
225

226
		ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: any; }) => {
227
			if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) {
J
Joao Moreno 已提交
228
				this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
229 230 231 232

				// Handle specific events on main side
				this.onBroadcast(broadcast.channel, broadcast.payload);

233 234
				// Send to all windows (except sender window)
				this.windowsMainService.sendToAll('vscode:broadcast', broadcast, [windowId]);
235 236 237
			}
		});

238
		ipc.on('vscode:labelRegisterFormatter', (event: any, data: RegisterFormatterEvent) => {
239
			this.labelService.registerFormatter(data.scheme, data.formatter);
240 241
		});

242 243 244 245 246 247 248 249 250 251 252 253
		ipc.on('vscode:toggleDevTools', (event: Event) => {
			event.sender.toggleDevTools();
		});

		ipc.on('vscode:openDevTools', (event: Event) => {
			event.sender.openDevTools();
		});

		ipc.on('vscode:reloadWindow', (event: Event) => {
			event.sender.reload();
		});

254
		// Keyboard layout changes
255
		KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
256
			if (this.windowsMainService) {
257
				this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
258 259 260 261
			}
		});
	}

262 263 264 265 266 267 268 269 270
	private isValidWebviewSource(source: string): boolean {
		if (!source) {
			return false;
		}

		if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
			return true;
		}

271 272
		const srcUri: any = URI.parse(source).fsPath.toLowerCase();
		const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
273
		return srcUri.startsWith(rootUri + nativeSep);
274 275
	}

B
Benjamin Pasero 已提交
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	private onUnexpectedError(err: Error): void {
		if (err) {

			// take only the message and stack property
			const friendlyError = {
				message: err.message,
				stack: err.stack
			};

			// handle on client side
			if (this.windowsMainService) {
				this.windowsMainService.sendToFocused('vscode:reportError', JSON.stringify(friendlyError));
			}
		}

		this.logService.error(`[uncaught exception in main]: ${err}`);
		if (err.stack) {
			this.logService.error(err.stack);
		}
	}

297 298 299 300 301 302
	private onBroadcast(event: string, payload: any): void {

		// Theme changes
		if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
			let data = JSON.parse(payload);

303 304
			this.stateService.setItem(THEME_STORAGE_KEY, data.baseTheme);
			this.stateService.setItem(THEME_BG_STORAGE_KEY, data.background);
305
		}
306 307
	}

B
Benjamin Pasero 已提交
308
	startup(): TPromise<void> {
J
Joao Moreno 已提交
309 310 311
		this.logService.debug('Starting VS Code');
		this.logService.debug(`from: ${this.environmentService.appRoot}`);
		this.logService.debug('args:', this.environmentService.args);
312

313 314 315 316 317 318 319
		// Make sure we associate the program with the app user model id
		// This will help Windows to associate the running program with
		// any shortcut that is pinned to the taskbar and prevent showing
		// two icons in the taskbar for the same app.
		if (platform.isWindows && product.win32AppUserModelId) {
			app.setAppUserModelId(product.win32AppUserModelId);
		}
320

321 322 323 324 325 326 327 328
		// Fix native tabs on macOS 10.13
		// macOS enables a compatibility patch for any bundle ID beginning with
		// "com.microsoft.", which breaks native tabs for VS Code when using this
		// identifier (from the official build).
		// Explicitly opt out of the patch here before creating any windows.
		// See: https://github.com/Microsoft/vscode/issues/35361#issuecomment-399794085
		try {
			if (platform.isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
329
				systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any);
330 331 332 333 334
			}
		} catch (error) {
			this.logService.error(error);
		}

335 336
		// Create Electron IPC Server
		this.electronIpcServer = new ElectronIPCServer();
337

338 339 340 341
		// Resolve unique machine ID
		this.logService.trace('Resolving machine identifier...');
		return this.resolveMachineId().then(machineId => {
			this.logService.trace(`Resolved machine identifier: ${machineId}`);
342

343
			// Spawn shared process
344
			this.sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
345
			this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
346

347 348
			// Services
			const appInstantiationService = this.initServices(machineId);
J
Joao Moreno 已提交
349

350
			let promise: TPromise<any> = TPromise.as(null);
351

352 353
			// Create driver
			if (this.environmentService.driverHandle) {
354
				serveDriver(this.electronIpcServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService).then(server => {
355 356 357 358
					this.logService.info('Driver started at:', this.environmentService.driverHandle);
					this.toDispose.push(server);
				});
			}
359

360
			return promise.then(() => {
361

362 363 364
				// Setup Auth Handler
				const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
				this.toDispose.push(authHandler);
365

366
				// Open Windows
B
Benjamin Pasero 已提交
367
				const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
B
Benjamin Pasero 已提交
368

369 370
				// Post Open Windows Tasks
				appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
B
Benjamin Pasero 已提交
371 372 373 374 375 376 377 378 379 380 381 382 383

				// Tracing: Stop tracing after windows are ready if enabled
				if (this.environmentService.args.trace) {
					this.logService.info(`Tracing: waiting for windows to get ready...`);

					let recordingStopped = false;
					const stopRecording = (timeout) => {
						if (recordingStopped) {
							return;
						}

						recordingStopped = true; // only once

B
Benjamin Pasero 已提交
384 385 386 387 388 389 390 391
						contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace`), path => {
							if (!timeout) {
								this.windowsMainService.showMessageBox({
									type: 'info',
									message: localize('trace.message', "Successfully created trace."),
									detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
									buttons: [localize('trace.ok', "Ok")]
								}, this.windowsMainService.getLastActiveWindow());
B
Benjamin Pasero 已提交
392
							} else {
B
Benjamin Pasero 已提交
393
								this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
B
Benjamin Pasero 已提交
394 395 396 397 398 399 400 401 402 403 404 405 406
							}
						});
					};

					// Wait up to 30s before creating the trace anyways
					const timeoutHandle = setTimeout(() => stopRecording(true), 30000);

					// Wait for all windows to get ready and stop tracing then
					TPromise.join(windows.map(window => window.ready())).then(() => {
						clearTimeout(timeoutHandle);
						stopRecording(false);
					});
				}
407
			});
408
		});
409 410
	}

411
	private resolveMachineId(): TPromise<string> {
B
Benjamin Pasero 已提交
412
		const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
413 414 415 416 417 418 419
		if (machineId) {
			return TPromise.wrap(machineId);
		}

		return getMachineId().then(machineId => {

			// Remember in global storage
B
Benjamin Pasero 已提交
420
			this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);
421 422 423 424 425 426

			return machineId;
		});
	}

	private initServices(machineId: string): IInstantiationService {
427 428
		const services = new ServiceCollection();

429 430 431 432 433 434 435 436
		if (process.platform === 'win32') {
			services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
		} else if (process.platform === 'linux') {
			services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
		} else if (process.platform === 'darwin') {
			services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
		}

437
		services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, machineId));
438 439
		services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess));
		services.set(ILaunchService, new SyncDescriptor(LaunchService));
440
		services.set(IIssueService, new SyncDescriptor(IssueService, machineId, this.userEnv));
441
		services.set(IMenubarService, new SyncDescriptor(MenubarService));
442 443

		// Telemtry
444
		if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
445
			const channel = getDelayedChannel<ITelemetryAppenderChannel>(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender')));
446
			const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
447
			const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath);
448 449
			const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
			const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
450

451 452 453 454 455 456 457 458
			services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config));
		} else {
			services.set(ITelemetryService, NullTelemetryService);
		}

		return this.instantiationService.createChild(services);
	}

B
Benjamin Pasero 已提交
459
	private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] {
460 461 462 463 464 465 466 467 468 469 470 471
		const appInstantiationService = accessor.get(IInstantiationService);

		// Register more Main IPC services
		const launchService = accessor.get(ILaunchService);
		const launchChannel = new LaunchChannel(launchService);
		this.mainIpcServer.registerChannel('launch', launchChannel);

		// Register more Electron IPC services
		const updateService = accessor.get(IUpdateService);
		const updateChannel = new UpdateChannel(updateService);
		this.electronIpcServer.registerChannel('update', updateChannel);

472 473 474 475
		const issueService = accessor.get(IIssueService);
		const issueChannel = new IssueChannel(issueService);
		this.electronIpcServer.registerChannel('issue', issueChannel);

B
Benjamin Pasero 已提交
476 477 478 479
		const workspacesService = accessor.get(IWorkspacesMainService);
		const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService);
		this.electronIpcServer.registerChannel('workspaces', workspacesChannel);

480 481 482
		const windowsService = accessor.get(IWindowsService);
		const windowsChannel = new WindowsChannel(windowsService);
		this.electronIpcServer.registerChannel('windows', windowsChannel);
483
		this.sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
484

485 486 487 488
		const menubarService = accessor.get(IMenubarService);
		const menubarChannel = new MenubarChannel(menubarService);
		this.electronIpcServer.registerChannel('menubar', menubarChannel);

J
Joao Moreno 已提交
489 490 491 492
		const urlService = accessor.get(IURLService);
		const urlChannel = new URLServiceChannel(urlService);
		this.electronIpcServer.registerChannel('url', urlChannel);

S
Sandeep Somavarapu 已提交
493
		// Log level management
494
		const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
S
Sandeep Somavarapu 已提交
495
		this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
496
		this.sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
S
Sandeep Somavarapu 已提交
497

498 499 500 501
		// Lifecycle
		this.lifecycleService.ready();

		// Propagate to clients
502
		const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
J
Joao Moreno 已提交
503 504 505

		const args = this.environmentService.args;

J
Joao Moreno 已提交
506
		// Create a URL handler which forwards to the last active window
J
Joao Moreno 已提交
507
		const activeWindowManager = new ActiveWindowManager(windowsService);
J
Joao Moreno 已提交
508
		const route = () => activeWindowManager.getActiveClientId();
J
Joao Moreno 已提交
509
		const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { routeCall: route, routeEvent: route });
J
Joao Moreno 已提交
510
		const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
J
Joao Moreno 已提交
511

512 513 514 515 516 517
		// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
		// if there is none
		if (platform.isMacintosh) {
			const environmentService = accessor.get(IEnvironmentService);

			urlService.registerHandler({
J
Joao Moreno 已提交
518
				handleURL(uri: URI): TPromise<boolean> {
519 520 521 522 523 524 525
					if (windowsMainService.getWindowCount() === 0) {
						const cli = { ...environmentService.args, goto: true };
						const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true });

						return window.ready().then(() => urlService.open(uri));
					}

J
Joao Moreno 已提交
526
					return TPromise.as(false);
527 528 529 530
				}
			});
		}

J
Joao Moreno 已提交
531
		// Register the multiple URL handker
J
Joao Moreno 已提交
532 533
		urlService.registerHandler(multiplexURLHandler);

J
Joao Moreno 已提交
534
		// Watch Electron URLs and forward them to the UrlService
J
Joao Moreno 已提交
535 536 537 538
		const urls = args['open-url'] ? args._urls : [];
		const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService);
		this.toDispose.push(urlListener);

539 540 541
		this.windowsMainService.ready(this.userEnv);

		// Open our first window
542
		const macOpenFiles = (<any>global).macOpenFiles as string[];
543
		const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
M
Martin Aeschlimann 已提交
544 545 546
		const hasCliArgs = hasArgs(args._);
		const hasFolderURIs = hasArgs(args['folder-uri']);
		const hasFileURIs = hasArgs(args['file-uri']);
547

M
Martin Aeschlimann 已提交
548
		if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
B
Benjamin Pasero 已提交
549
			return this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
550
		}
B
Benjamin Pasero 已提交
551 552 553 554 555 556

		if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
			return this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => URI.file(file)), initialStartup: true }); // mac: open-file event received on startup
		}

		return this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli
557 558 559
	}

	private afterWindowOpen(accessor: ServicesAccessor): void {
560
		const windowsMainService = accessor.get(IWindowsMainService);
561 562 563

		let windowsMutex: Mutex = null;
		if (platform.isWindows) {
564 565

			// Setup Windows mutex
566 567 568 569 570
			try {
				const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex;
				windowsMutex = new Mutex(product.win32MutexName);
				this.toDispose.push({ dispose: () => windowsMutex.release() });
			} catch (e) {
571
				if (!this.environmentService.isBuilt) {
572
					windowsMainService.showMessageBox({
573 574 575 576 577
						title: product.nameLong,
						type: 'warning',
						message: 'Failed to load windows-mutex!',
						detail: e.toString(),
						noLink: true
578 579
					});
				}
580
			}
581

582
			// Ensure Windows foreground love module
583
			try {
584
				// tslint:disable-next-line:no-unused-expression
585 586 587
				<any>require.__$__nodeRequire('windows-foreground-love');
			} catch (e) {
				if (!this.environmentService.isBuilt) {
588
					windowsMainService.showMessageBox({
589 590 591 592 593
						title: product.nameLong,
						type: 'warning',
						message: 'Failed to load windows-foreground-love!',
						detail: e.toString(),
						noLink: true
594 595 596
					});
				}
			}
597
		}
598

599 600 601 602
		// TODO@sbatten: Remove when switching back to dynamic menu
		// Install Menu
		const instantiationService = accessor.get(IInstantiationService);
		const configurationService = accessor.get(IConfigurationService);
603 604 605 606 607 608 609 610 611

		let createNativeMenu = true;
		if (platform.isLinux) {
			createNativeMenu = configurationService.getValue<string>('window.titleBarStyle') !== 'custom';
		} else if (platform.isWindows) {
			createNativeMenu = configurationService.getValue<string>('window.titleBarStyle') === 'native';
		}

		if (createNativeMenu) {
612 613 614
			instantiationService.createInstance(CodeMenu);
		}

615
		// Jump List
B
Benjamin Pasero 已提交
616 617
		this.historyMainService.updateWindowsJumpList();
		this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList());
618

619
		// Start shared process after a while
620 621 622
		const sharedProcess = new RunOnceScheduler(() => this.sharedProcess.spawn(), 3000);
		sharedProcess.schedule();
		this.toDispose.push(sharedProcess);
623 624 625 626 627
	}

	private dispose(): void {
		this.toDispose = dispose(this.toDispose);
	}
628
}