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

A
Tweaks  
Alex Dima 已提交
6
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol } from 'electron';
7
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
8
import { WindowsManager } from 'vs/code/electron-main/windows';
J
Joao Moreno 已提交
9
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
J
Joao Moreno 已提交
10
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
11
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
B
Benjamin Pasero 已提交
12
import { ILifecycleService, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
B
Benjamin Pasero 已提交
13
import { getShellEnvironment } from 'vs/code/node/shellEnv';
14
import { IUpdateService } from 'vs/platform/update/common/update';
J
Joao Moreno 已提交
15
import { UpdateChannel } from 'vs/platform/update/node/updateIpc';
16 17 18 19
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 已提交
20
import { LaunchService, LaunchChannel, ILaunchService } from 'vs/platform/launch/electron-main/launchService';
21 22 23
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';
24
import { ILogService } from 'vs/platform/log/common/log';
B
Benjamin Pasero 已提交
25
import { IStateService } from 'vs/platform/state/common/state';
26 27 28
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 已提交
29
import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/node/urlIpc';
30
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
31
import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils';
J
Joao Moreno 已提交
32
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
33
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
34
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
J
Joao Moreno 已提交
35
import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/node/ipc';
36 37
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
38
import { ProxyAuthHandler } from 'vs/code/electron-main/auth';
39
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
B
Benjamin Pasero 已提交
40
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
B
Benjamin Pasero 已提交
41
import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows';
B
Benjamin Pasero 已提交
42
import { IHistoryMainService } from 'vs/platform/history/common/history';
B
Benjamin Pasero 已提交
43
import { isUndefinedOrNull } from 'vs/base/common/types';
44
import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard';
45
import { URI } from 'vs/base/common/uri';
J
Joao Moreno 已提交
46
import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc';
B
Benjamin Pasero 已提交
47
import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces';
48
import { getMachineId } from 'vs/base/node/id';
49 50 51
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';
52
import { IIssueService } from 'vs/platform/issue/common/issue';
J
Joao Moreno 已提交
53
import { IssueChannel } from 'vs/platform/issue/node/issueIpc';
P
Pine Wu 已提交
54
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
J
Joao Moreno 已提交
55
import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc';
A
Alex Dima 已提交
56
import * as errors from 'vs/base/common/errors';
J
Joao Moreno 已提交
57
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
J
Joao Moreno 已提交
58
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
A
Alex Dima 已提交
59
import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection';
60 61
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
J
Joao Moreno 已提交
62
import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc';
M
Martin Aeschlimann 已提交
63
import { hasArgs } from 'vs/platform/environment/node/argv';
64
import { RunOnceScheduler } from 'vs/base/common/async';
65
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
66
import { storeBackgroundColor } from 'vs/code/electron-main/theme';
B
Benjamin Pasero 已提交
67 68 69
import { nativeSep, join } from 'vs/base/common/paths';
import { homedir } from 'os';
import { localize } from 'vs/nls';
A
Tweaks  
Alex Dima 已提交
70
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
J
Joao Moreno 已提交
71
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/remote/node/remoteAgentFileSystemChannel';
A
Tweaks  
Alex Dima 已提交
72
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
J
Joao Moreno 已提交
73
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
B
wip  
Benjamin Pasero 已提交
74 75
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
76
import { generateUuid } from 'vs/base/common/uuid';
77
import { startsWith } from 'vs/base/common/strings';
78 79 80 81 82
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService';
import { URLService } from 'vs/platform/url/common/urlService';
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
83

84
export class CodeApplication extends Disposable {
B
Benjamin Pasero 已提交
85

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

88 89 90 91 92
	private windowsMainService: IWindowsMainService;

	private electronIpcServer: ElectronIPCServer;

	private sharedProcess: SharedProcess;
J
Johannes Rieken 已提交
93
	private sharedProcessClient: Promise<Client>;
94 95 96

	constructor(
		private mainIpcServer: Server,
97
		private userEnv: IProcessEnvironment,
98 99 100 101
		@IInstantiationService private instantiationService: IInstantiationService,
		@ILogService private logService: ILogService,
		@IEnvironmentService private environmentService: IEnvironmentService,
		@ILifecycleService private lifecycleService: ILifecycleService,
102
		@IConfigurationService private configurationService: ConfigurationService,
103
		@IStateService private stateService: IStateService
104
	) {
105 106 107 108
		super();

		this._register(mainIpcServer);
		this._register(configurationService);
109 110 111 112 113 114 115

		this.registerListeners();
	}

	private registerListeners(): void {

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

120 121 122
		// Contextmenu via IPC support
		registerContextMenuListener();

B
Benjamin Pasero 已提交
123 124
		// Dispose on shutdown
		this.lifecycleService.onWillShutdown(() => this.dispose());
125

126 127 128 129 130 131 132
		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 已提交
133
			this.logService.trace('App#activate');
134 135 136 137 138 139 140

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

141 142
		// Security related measures (https://electronjs.org/docs/tutorial/security)
		// DO NOT CHANGE without consulting the documentation
143
		app.on('web-contents-created', (event: any, contents) => {
144
			contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => {
145

146 147 148 149 150 151 152 153 154
				const 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;
					}

155
					const srcUri = URI.parse(source).fsPath.toLowerCase();
156 157
					const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();

158
					return startsWith(srcUri, rootUri + nativeSep);
159 160
				};

161
				// Ensure defaults
M
Matt Bierner 已提交
162 163 164 165
				delete webPreferences.preload;
				webPreferences.nodeIntegration = false;

				// Verify URLs being loaded
166
				if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) {
M
Matt Bierner 已提交
167 168
					return;
				}
M
Matt Bierner 已提交
169

170 171
				delete webPreferences.preloadUrl;

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

M
Matt Bierner 已提交
175 176 177 178
				event.preventDefault();
			});

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

M
Matt Bierner 已提交
181 182
				event.preventDefault();
			});
183 184 185 186 187 188

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

				shell.openExternal(url);
			});
M
Matt Bierner 已提交
189 190
		});

191
		let macOpenFileURIs: URI[] = [];
192
		let runningTimeout: any = null;
193
		app.on('open-file', (event: Event, path: string) => {
J
Joao Moreno 已提交
194
			this.logService.trace('App#open-file: ', path);
195 196 197
			event.preventDefault();

			// Keep in array because more might come!
198
			macOpenFileURIs.push(URI.file(path));
199 200 201 202 203 204 205 206 207 208 209 210 211

			// 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 已提交
212
						urisToOpen: macOpenFileURIs,
213 214
						preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
					});
215
					macOpenFileURIs = [];
216 217 218 219 220
					runningTimeout = null;
				}
			}, 100);
		});

221 222 223 224
		app.on('new-window-for-tab', () => {
			this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button
		});

225
		ipc.on('vscode:exit', (event: Event, code: number) => {
J
Joao Moreno 已提交
226
			this.logService.trace('IPC#vscode:exit', code);
227 228 229 230 231

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

232 233
		ipc.on('vscode:fetchShellEnv', (event: Event) => {
			const webContents = event.sender;
234
			getShellEnvironment().then(shellEnv => {
J
Johannes Rieken 已提交
235 236 237
				if (!webContents.isDestroyed()) {
					webContents.send('vscode:acceptShellEnv', shellEnv);
				}
238
			}, err => {
J
Johannes Rieken 已提交
239 240 241
				if (!webContents.isDestroyed()) {
					webContents.send('vscode:acceptShellEnv', {});
				}
B
Benjamin Pasero 已提交
242 243

				this.logService.error('Error fetching shell env', err);
244 245
			});
		});
246

247
		ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: any; }) => {
248
			if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) {
J
Joao Moreno 已提交
249
				this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload);
250 251 252 253

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

254 255
				// Send to all windows (except sender window)
				this.windowsMainService.sendToAll('vscode:broadcast', broadcast, [windowId]);
256 257 258
			}
		});

259 260
		ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools());
		ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools());
B
Benjamin Pasero 已提交
261

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

B
Benjamin Pasero 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	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);
		}
	}

286 287 288 289
	private onBroadcast(event: string, payload: any): void {

		// Theme changes
		if (event === 'vscode:changeColorTheme' && typeof payload === 'string') {
290
			storeBackgroundColor(this.stateService, JSON.parse(payload));
291
		}
292 293
	}

J
Johannes Rieken 已提交
294
	startup(): Promise<void> {
J
Joao Moreno 已提交
295 296 297
		this.logService.debug('Starting VS Code');
		this.logService.debug(`from: ${this.environmentService.appRoot}`);
		this.logService.debug('args:', this.environmentService.args);
298

299 300 301 302
		// 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.
303
		if (isWindows && product.win32AppUserModelId) {
304 305
			app.setAppUserModelId(product.win32AppUserModelId);
		}
306

307 308 309 310 311 312 313
		// 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 {
314
			if (isMacintosh && this.configurationService.getValue<boolean>('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
315
				systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true as any);
316 317 318 319 320
			}
		} catch (error) {
			this.logService.error(error);
		}

321 322
		// Create Electron IPC Server
		this.electronIpcServer = new ElectronIPCServer();
323

B
Benjamin Pasero 已提交
324
		const startupWithMachineId = (machineId: string) => {
325
			this.logService.trace(`Resolved machine identifier: ${machineId}`);
326

327
			// Spawn shared process
328
			this.sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv);
329
			this.sharedProcessClient = this.sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main'));
330

331
			// Services
332
			return this.initServices(machineId).then(appInstantiationService => {
333

334 335 336 337 338 339 340
				// Create driver
				if (this.environmentService.driverHandle) {
					serveDriver(this.electronIpcServer, this.environmentService.driverHandle, this.environmentService, appInstantiationService).then(server => {
						this.logService.info('Driver started at:', this.environmentService.driverHandle);
						this._register(server);
					});
				}
341

342 343
				// Setup Auth Handler
				const authHandler = appInstantiationService.createInstance(ProxyAuthHandler);
344
				this._register(authHandler);
345

346
				// Open Windows
B
Benjamin Pasero 已提交
347
				const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor));
B
Benjamin Pasero 已提交
348

349 350
				// Post Open Windows Tasks
				appInstantiationService.invokeFunction(accessor => this.afterWindowOpen(accessor));
B
Benjamin Pasero 已提交
351 352 353

				// Tracing: Stop tracing after windows are ready if enabled
				if (this.environmentService.args.trace) {
354 355 356
					this.stopTracingEventually(windows);
				}
			});
B
Benjamin Pasero 已提交
357 358 359 360 361 362 363 364 365 366 367 368
		};

		// Resolve unique machine ID
		this.logService.trace('Resolving machine identifier...');
		const resolvedMachineId = this.resolveMachineId();
		if (typeof resolvedMachineId === 'string') {
			return startupWithMachineId(resolvedMachineId);
		} else {
			return resolvedMachineId.then(machineId => startupWithMachineId(machineId));
		}
	}

J
Johannes Rieken 已提交
369
	private resolveMachineId(): string | Promise<string> {
B
Benjamin Pasero 已提交
370 371 372 373 374 375 376 377 378
		const machineId = this.stateService.getItem<string>(CodeApplication.MACHINE_ID_KEY);
		if (machineId) {
			return machineId;
		}

		return getMachineId().then(machineId => {
			this.stateService.setItem(CodeApplication.MACHINE_ID_KEY, machineId);

			return machineId;
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
		});
	}

	private stopTracingEventually(windows: ICodeWindow[]): void {
		this.logService.info(`Tracing: waiting for windows to get ready...`);

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

			recordingStopped = true; // only once

			contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), 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());
				} else {
					this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`);
B
Benjamin Pasero 已提交
403
				}
404
			});
405 406 407 408 409 410
		};

		// 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
B
Benjamin Pasero 已提交
411
		Promise.all(windows.map(window => window.ready())).then(() => {
412 413
			clearTimeout(timeoutHandle);
			stopRecording(false);
414
		});
415 416
	}

J
Johannes Rieken 已提交
417
	private initServices(machineId: string): Promise<IInstantiationService> {
418 419
		const services = new ServiceCollection();

420 421 422
		if (process.platform === 'win32') {
			services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
		} else if (process.platform === 'linux') {
J
Joao Moreno 已提交
423
			if (process.env.SNAP && process.env.SNAP_REVISION) {
J
Joao Moreno 已提交
424
				services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env.SNAP, process.env.SNAP_REVISION]));
J
Joao Moreno 已提交
425 426 427
			} else {
				services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
			}
428 429 430 431
		} else if (process.platform === 'darwin') {
			services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
		}

432 433
		services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId]));
		services.set(IWindowsService, new SyncDescriptor(WindowsService, [this.sharedProcess]));
434
		services.set(ILaunchService, new SyncDescriptor(LaunchService));
435
		services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv]));
436
		services.set(IMenubarService, new SyncDescriptor(MenubarService));
437
		services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
438 439 440 441
		services.set(IBackupMainService, new SyncDescriptor(BackupMainService));
		services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService));
		services.set(IURLService, new SyncDescriptor(URLService));
		services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService));
442

443
		// Telemetry
444
		if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
J
Joao Moreno 已提交
445
			const channel = getDelayedChannel(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
			services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
452 453 454 455
		} else {
			services.set(ITelemetryService, NullTelemetryService);
		}

456 457 458 459 460
		const appInstantiationService = this.instantiationService.createChild(services);

		return appInstantiationService.invokeFunction(accessor => this.initStorageService(accessor)).then(() => appInstantiationService);
	}

J
Johannes Rieken 已提交
461
	private initStorageService(accessor: ServicesAccessor): Promise<void> {
462
		const storageMainService = accessor.get(IStorageMainService) as StorageMainService;
463

B
wip  
Benjamin Pasero 已提交
464
		// Ensure to close storage on shutdown
465
		this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close()));
B
wip  
Benjamin Pasero 已提交
466

467
		// Initialize storage service
468
		return storageMainService.initialize().then(void 0, error => {
469 470
			errors.onUnexpectedError(error);
			this.logService.error(error);
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
		}).then(() => {

			// Apply global telemetry values as part of the initialization
			// These are global across all windows and thereby should be
			// written from the main process once.

			const telemetryInstanceId = 'telemetry.instanceId';
			const instanceId = storageMainService.get(telemetryInstanceId, null);
			if (instanceId === null) {
				storageMainService.store(telemetryInstanceId, generateUuid());
			}

			const telemetryFirstSessionDate = 'telemetry.firstSessionDate';
			const firstSessionDate = storageMainService.get(telemetryFirstSessionDate, null);
			if (firstSessionDate === null) {
				storageMainService.store(telemetryFirstSessionDate, new Date().toUTCString());
			}

			const telemetryCurrentSessionDate = 'telemetry.currentSessionDate';
			const telemetryLastSessionDate = 'telemetry.lastSessionDate';
			const lastSessionDate = storageMainService.get(telemetryCurrentSessionDate, null); // previous session date was the "current" one at that time
			const currentSessionDate = new Date().toUTCString(); // current session date is "now"
			storageMainService.store(telemetryLastSessionDate, lastSessionDate);
			storageMainService.store(telemetryCurrentSessionDate, currentSessionDate);
495
		});
496 497
	}

B
Benjamin Pasero 已提交
498
	private openFirstWindow(accessor: ServicesAccessor): ICodeWindow[] {
499 500 501 502 503 504 505 506 507 508 509 510
		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);

511 512 513 514
		const issueService = accessor.get(IIssueService);
		const issueChannel = new IssueChannel(issueService);
		this.electronIpcServer.registerChannel('issue', issueChannel);

B
Benjamin Pasero 已提交
515 516 517 518
		const workspacesService = accessor.get(IWorkspacesMainService);
		const workspacesChannel = appInstantiationService.createInstance(WorkspacesChannel, workspacesService);
		this.electronIpcServer.registerChannel('workspaces', workspacesChannel);

519 520 521
		const windowsService = accessor.get(IWindowsService);
		const windowsChannel = new WindowsChannel(windowsService);
		this.electronIpcServer.registerChannel('windows', windowsChannel);
522
		this.sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
523

524 525 526 527
		const menubarService = accessor.get(IMenubarService);
		const menubarChannel = new MenubarChannel(menubarService);
		this.electronIpcServer.registerChannel('menubar', menubarChannel);

J
Joao Moreno 已提交
528 529 530 531
		const urlService = accessor.get(IURLService);
		const urlChannel = new URLServiceChannel(urlService);
		this.electronIpcServer.registerChannel('url', urlChannel);

532
		const storageMainService = accessor.get(IStorageMainService);
533
		const storageChannel = this._register(new GlobalStorageDatabaseChannel(storageMainService as StorageMainService));
B
wip  
Benjamin Pasero 已提交
534 535
		this.electronIpcServer.registerChannel('storage', storageChannel);

S
Sandeep Somavarapu 已提交
536
		// Log level management
537
		const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
S
Sandeep Somavarapu 已提交
538
		this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
539
		this.sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
S
Sandeep Somavarapu 已提交
540

541
		// Lifecycle
B
Benjamin Pasero 已提交
542
		(this.lifecycleService as LifecycleService).ready();
543 544

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

J
Joao Moreno 已提交
547
		// Create a URL handler which forwards to the last active window
J
Joao Moreno 已提交
548
		const activeWindowManager = new ActiveWindowManager(windowsService);
J
Joao Moreno 已提交
549 550
		const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
		const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', activeWindowRouter);
J
Joao Moreno 已提交
551
		const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
J
Joao Moreno 已提交
552

553 554
		// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
		// if there is none
555
		if (isMacintosh) {
556 557 558
			const environmentService = accessor.get(IEnvironmentService);

			urlService.registerHandler({
J
Johannes Rieken 已提交
559
				handleURL(uri: URI): Promise<boolean> {
560 561 562 563 564 565 566
					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));
					}

B
Benjamin Pasero 已提交
567
					return Promise.resolve(false);
568 569 570 571
				}
			});
		}

J
Joao Moreno 已提交
572
		// Register the multiple URL handker
J
Joao Moreno 已提交
573 574
		urlService.registerHandler(multiplexURLHandler);

J
Joao Moreno 已提交
575
		// Watch Electron URLs and forward them to the UrlService
576
		const args = this.environmentService.args;
J
Joao Moreno 已提交
577 578
		const urls = args['open-url'] ? args._urls : [];
		const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService);
579
		this._register(urlListener);
J
Joao Moreno 已提交
580

581 582 583
		this.windowsMainService.ready(this.userEnv);

		// Open our first window
584
		const macOpenFiles = (<any>global).macOpenFiles as string[];
585
		const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
M
Martin Aeschlimann 已提交
586 587 588
		const hasCliArgs = hasArgs(args._);
		const hasFolderURIs = hasArgs(args['folder-uri']);
		const hasFileURIs = hasArgs(args['file-uri']);
589

M
Martin Aeschlimann 已提交
590
		if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
B
Benjamin Pasero 已提交
591
			return this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
592
		}
B
Benjamin Pasero 已提交
593 594 595 596 597 598

		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
599 600 601
	}

	private afterWindowOpen(accessor: ServicesAccessor): void {
602
		const windowsMainService = accessor.get(IWindowsMainService);
603
		const historyMainService = accessor.get(IHistoryMainService);
604

605
		let windowsMutex: Mutex | null = null;
606
		if (isWindows) {
607 608

			// Setup Windows mutex
609 610 611
			try {
				const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex;
				windowsMutex = new Mutex(product.win32MutexName);
612
				this._register(toDisposable(() => windowsMutex.release()));
613
			} catch (e) {
614
				if (!this.environmentService.isBuilt) {
615
					windowsMainService.showMessageBox({
616 617 618 619 620
						title: product.nameLong,
						type: 'warning',
						message: 'Failed to load windows-mutex!',
						detail: e.toString(),
						noLink: true
621 622
					});
				}
623
			}
624

625
			// Ensure Windows foreground love module
626
			try {
627
				// tslint:disable-next-line:no-unused-expression
628
				require.__$__nodeRequire('windows-foreground-love');
629 630
			} catch (e) {
				if (!this.environmentService.isBuilt) {
631
					windowsMainService.showMessageBox({
632 633 634 635 636
						title: product.nameLong,
						type: 'warning',
						message: 'Failed to load windows-foreground-love!',
						detail: e.toString(),
						noLink: true
637 638 639
					});
				}
			}
640
		}
641

642 643 644 645 646 647 648 649
		// Remote Authorities
		this.handleRemoteAuthorities();

		// Keyboard layout changes
		KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => {
			this.windowsMainService.sendToAll('vscode:keyboardLayoutChanged', false);
		});

650
		// Jump List
651 652
		historyMainService.updateWindowsJumpList();
		historyMainService.onRecentlyOpenedChange(() => historyMainService.updateWindowsJumpList());
653

B
Benjamin Pasero 已提交
654 655
		// Start shared process after a while
		const sharedProcessSpawn = this._register(new RunOnceScheduler(() => getShellEnvironment().then(userEnv => this.sharedProcess.spawn(userEnv)), 3000));
656
		sharedProcessSpawn.schedule();
657
	}
658 659 660 661

	private handleRemoteAuthorities(): void {
		const connectionPool: Map<string, ActiveConnection> = new Map<string, ActiveConnection>();

A
Alex Dima 已提交
662 663
		const isBuilt = this.environmentService.isBuilt;

664 665 666 667 668 669 670
		class ActiveConnection {
			private _authority: string;
			private _client: Promise<Client<RemoteAgentConnectionContext>>;
			private _disposeRunner: RunOnceScheduler;

			constructor(authority: string, host: string, port: number) {
				this._authority = authority;
A
Alex Dima 已提交
671
				this._client = connectRemoteAgentManagement(authority, host, port, `main`, isBuilt);
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
				this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000);
			}

			private _dispose(): void {
				this._disposeRunner.dispose();
				connectionPool.delete(this._authority);
				this._client.then((connection) => {
					connection.dispose();
				});
			}

			public getClient(): Promise<Client<RemoteAgentConnectionContext>> {
				this._disposeRunner.schedule();
				return this._client;
			}
		}

		const resolvedAuthorities = new Map<string, ResolvedAuthority>();
		ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => {
			resolvedAuthorities.set(data.authority, data);
		});

		const resolveAuthority = (authority: string): ResolvedAuthority | null => {
			if (authority.indexOf('+') >= 0) {
				if (resolvedAuthorities.has(authority)) {
					return resolvedAuthorities.get(authority);
				}
				return null;
			} else {
				const [host, strPort] = authority.split(':');
				const port = parseInt(strPort, 10);
				return { authority, host, port, syncExtensions: false };
			}
		};

		protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => {
			if (request.method !== 'GET') {
				return callback(null);
			}
			const uri = URI.parse(request.url);

			let activeConnection: ActiveConnection = null;
			if (connectionPool.has(uri.authority)) {
				activeConnection = connectionPool.get(uri.authority);
			} else {
				let resolvedAuthority = resolveAuthority(uri.authority);
				if (!resolvedAuthority) {
					callback(null);
					return;
				}
				activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port);
				connectionPool.set(uri.authority, activeConnection);
			}
			try {
				const rawClient = await activeConnection.getClient();
				if (connectionPool.has(uri.authority)) { // not disposed in the meantime
					const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME);

					// TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider`
					const fileContents = await channel.call<Uint8Array>('readFile', [uri]);
					callback(Buffer.from(fileContents));
				} else {
					callback(null);
				}
			} catch (err) {
				errors.onUnexpectedError(err);
				callback(null);
			}
		});
	}
742
}
743