extHostExtensionService.ts 29.6 KB
Newer Older
E
Erich Gamma 已提交
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
Alex Dima 已提交
6
import * as nls from 'vs/nls';
7
import * as path from 'vs/base/common/path';
8
import { originalFSPath } from 'vs/base/common/resources';
A
Alex Dima 已提交
9
import { Barrier } from 'vs/base/common/async';
10
import { dispose, toDisposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
11 12 13
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
J
Johannes Rieken 已提交
14 15
import { createApiFactory, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
A
Tweaks  
Alex Dima 已提交
16
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol';
J
Johannes Rieken 已提交
17
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
18
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator';
J
Johannes Rieken 已提交
19
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
J
Johannes Rieken 已提交
20 21
import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
22
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
23
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
24
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
A
Alex Dima 已提交
25 26
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
A
Alex Dima 已提交
27
import * as vscode from 'vscode';
28
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
29
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
30
import { Schemas } from 'vs/base/common/network';
M
Matt Bierner 已提交
31
import { withNullAsUndefined } from 'vs/base/common/types';
32
import { VSBuffer } from 'vs/base/common/buffer';
33 34
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
35
import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes';
36
import { IURITransformer } from 'vs/base/common/uriIpc';
A
Alex Dima 已提交
37 38

interface ITestRunner {
P
Pine Wu 已提交
39
	/** Old test runner API, as exported from `vscode/lib/testrunner` */
A
Alex Dima 已提交
40 41 42
	run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}

P
Pine Wu 已提交
43 44 45
interface INewTestRunner {
	/** New test runner API, as explained in the extension test doc */
	run(): Promise<void>;
A
Alex Dima 已提交
46 47
}

J
Johannes Rieken 已提交
48 49 50 51 52 53
export interface IHostUtils {
	exit(code?: number): void;
	exists(path: string): Promise<boolean>;
	realpath(path: string): Promise<string>;
}

A
Alex Dima 已提交
54
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
A
Alex Dima 已提交
55

A
Alex Dima 已提交
56 57
	private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;

J
Johannes Rieken 已提交
58
	private readonly _hostUtils: IHostUtils;
A
Alex Dima 已提交
59 60 61 62
	private readonly _initData: IInitData;
	private readonly _extHostContext: IMainContext;
	private readonly _extHostWorkspace: ExtHostWorkspace;
	private readonly _extHostConfiguration: ExtHostConfiguration;
D
Dirk Baeumer 已提交
63
	private readonly _environment: IEnvironment;
A
Alex Dima 已提交
64 65 66 67 68 69
	private readonly _extHostLogService: ExtHostLogService;

	private readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
	private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
	private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;

A
Alex Dima 已提交
70
	private readonly _almostReadyToRunExtensions: Barrier;
S
Sandeep Somavarapu 已提交
71
	private readonly _readyToStartExtensionHost: Barrier;
A
Alex Dima 已提交
72
	private readonly _readyToRunExtensions: Barrier;
A
Alex Dima 已提交
73 74
	private readonly _registry: ExtensionDescriptionRegistry;
	private readonly _storage: ExtHostStorage;
75
	private readonly _storagePath: ExtensionStoragePaths;
A
Alex Dima 已提交
76
	private readonly _activator: ExtensionsActivator;
A
Alex Dima 已提交
77
	private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>> | null;
A
Alex Dima 已提交
78 79
	private readonly _extensionApiFactory: IExtensionApiFactory;

A
Alex Dima 已提交
80 81
	private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; };

A
Alex Dima 已提交
82 83 84
	private _started: boolean;

	constructor(
J
Johannes Rieken 已提交
85
		hostUtils: IHostUtils,
A
Alex Dima 已提交
86
		initData: IInitData,
87
		extHostContext: IMainContext,
88
		extHostWorkspace: ExtHostWorkspace,
J
Joao Moreno 已提交
89
		extHostConfiguration: ExtHostConfiguration,
D
Dirk Baeumer 已提交
90
		environment: IEnvironment,
A
Alex Dima 已提交
91
		extHostLogService: ExtHostLogService,
92
		uriTransformer: IURITransformer | null
93
	) {
J
Johannes Rieken 已提交
94
		this._hostUtils = hostUtils;
A
Alex Dima 已提交
95 96 97 98
		this._initData = initData;
		this._extHostContext = extHostContext;
		this._extHostWorkspace = extHostWorkspace;
		this._extHostConfiguration = extHostConfiguration;
D
Dirk Baeumer 已提交
99
		this._environment = environment;
A
Alex Dima 已提交
100 101 102 103 104 105
		this._extHostLogService = extHostLogService;

		this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
		this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
		this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);

A
Alex Dima 已提交
106
		this._almostReadyToRunExtensions = new Barrier();
S
Sandeep Somavarapu 已提交
107
		this._readyToStartExtensionHost = new Barrier();
A
Alex Dima 已提交
108
		this._readyToRunExtensions = new Barrier();
A
Alex Dima 已提交
109
		this._registry = new ExtensionDescriptionRegistry(initData.extensions);
A
Alex Dima 已提交
110
		this._storage = new ExtHostStorage(this._extHostContext);
111
		this._storagePath = new ExtensionStoragePaths(withNullAsUndefined(initData.workspace), initData.environment);
112 113 114 115 116

		const hostExtensions = new Set<string>();
		initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId)));

		this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, initData.hostExtensions, {
117 118
			onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
				this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
A
Alex Dima 已提交
119 120
			},

121 122
			actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
				if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
123
					const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
124 125 126
					await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent);
					return new HostExtension();
				}
A
Alex Dima 已提交
127
				const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
A
Alex Dima 已提交
128 129 130 131
				return this._activateExtension(extensionDescription, reason);
			}
		});
		this._extensionPathIndex = null;
132

A
Alex Dima 已提交
133
		// initialize API first (i.e. do not release barrier until the API is initialized)
A
Alex Dima 已提交
134 135 136 137 138 139 140 141
		this._extensionApiFactory = createApiFactory(
			this._initData,
			this._extHostContext,
			this._extHostWorkspace,
			this._extHostConfiguration,
			this,
			this._extHostLogService,
			this._storage,
142
			uriTransformer
A
Alex Dima 已提交
143
		);
144

A
Alex Dima 已提交
145 146
		this._resolvers = Object.create(null);

A
Alex Dima 已提交
147 148
		this._started = false;

149
		this._initialize();
A
Alex Dima 已提交
150 151 152 153

		if (this._initData.autoStart) {
			this._startExtensionHost();
		}
154 155
	}

156 157 158
	private async _initialize(): Promise<void> {
		try {
			const configProvider = await this._extHostConfiguration.getConfigProvider();
159 160
			const extensionPaths = await this.getExtensionPathIndex();
			NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider));
D
Dirk Baeumer 已提交
161
			NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._environment));
162
			if (this._initData.remote.isRemote) {
D
Dirk Baeumer 已提交
163 164 165 166 167 168
				NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory(
					this._extHostContext.getProxy(MainContext.MainThreadWindow),
					this._extHostContext.getProxy(MainContext.MainThreadTelemetry),
					extensionPaths
				));
			}
169

170
			// Do this when extension service exists, but extensions are not being activated yet.
171
			await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy);
A
Alex Dima 已提交
172 173 174
			this._almostReadyToRunExtensions.open();

			await this._extHostWorkspace.waitForInitializeCall();
S
Sandeep Somavarapu 已提交
175
			this._readyToStartExtensionHost.open();
176 177 178 179 180
		} catch (err) {
			errors.onUnexpectedError(err);
		}
	}

A
Alex Dima 已提交
181
	public async deactivateAll(): Promise<void> {
J
Johannes Rieken 已提交
182
		let allPromises: Promise<void>[] = [];
A
Alex Dima 已提交
183 184
		try {
			const allExtensions = this._registry.getAllExtensionDescriptions();
185
			const allExtensionsIds = allExtensions.map(ext => ext.identifier);
A
Alex Dima 已提交
186 187 188 189 190 191 192 193 194
			const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));

			allPromises = activatedExtensions.map((extensionId) => {
				return this._deactivate(extensionId);
			});
		} catch (err) {
			// TODO: write to log once we have one
		}
		await allPromises;
195 196
	}

197
	public isActivated(extensionId: ExtensionIdentifier): boolean {
A
Alex Dima 已提交
198
		if (this._readyToRunExtensions.isOpen()) {
A
Alex Dima 已提交
199 200 201
			return this._activator.isActivated(extensionId);
		}
		return false;
202 203
	}

J
Johannes Rieken 已提交
204
	private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
A
Alex Dima 已提交
205
		const reason = new ExtensionActivatedByEvent(startup, activationEvent);
A
Alex Dima 已提交
206
		return this._activator.activateByEvent(activationEvent, reason);
207 208
	}

209
	private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
A
Alex Dima 已提交
210
		return this._activator.activateById(extensionId, reason);
211 212
	}

213
	public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
A
Alex Dima 已提交
214
		return this._activateById(extensionId, reason).then(() => {
215 216 217
			const extension = this._activator.getActivatedExtension(extensionId);
			if (extension.activationFailed) {
				// activation failed => bubble up the error as the promise result
218
				return Promise.reject(extension.activationFailedError);
219
			}
R
Rob Lourens 已提交
220
			return undefined;
221 222 223
		});
	}

A
Alex Dima 已提交
224
	public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
A
Alex Dima 已提交
225
		return this._readyToRunExtensions.wait().then(_ => this._registry);
226 227
	}

A
Alex Dima 已提交
228
	public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
A
Alex Dima 已提交
229
		if (this._readyToRunExtensions.isOpen()) {
A
Alex Dima 已提交
230 231 232
			return this._activator.getActivatedExtension(extensionId).exports;
		} else {
			return null;
E
Erich Gamma 已提交
233 234 235
		}
	}

236
	// create trie to enable fast 'filename -> extension id' look up
237
	public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
238
		if (!this._extensionPathIndex) {
239
			const tree = TernarySearchTree.forPaths<IExtensionDescription>();
A
Alex Dima 已提交
240
			const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
241 242 243
				if (!ext.main) {
					return undefined;
				}
J
Johannes Rieken 已提交
244
				return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
245
			});
246
			this._extensionPathIndex = Promise.all(extensions).then(() => tree);
247 248 249 250
		}
		return this._extensionPathIndex;
	}

251
	private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {
R
Rob Lourens 已提交
252
		let result = Promise.resolve(undefined);
253

A
Alex Dima 已提交
254
		if (!this._readyToRunExtensions.isOpen()) {
A
Alex Dima 已提交
255 256 257 258
			return result;
		}

		if (!this._activator.isActivated(extensionId)) {
A
Alex Dima 已提交
259 260 261
			return result;
		}

262
		const extension = this._activator.getActivatedExtension(extensionId);
A
Alex Dima 已提交
263
		if (!extension) {
264
			return result;
265 266 267 268
		}

		// call deactivate if available
		try {
A
Alex Dima 已提交
269
			if (typeof extension.module.deactivate === 'function') {
R
Rob Lourens 已提交
270
				result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {
271
					// TODO: Do something with err if this is not the shutdown case
R
Rob Lourens 已提交
272
					return Promise.resolve(undefined);
273
				});
274
			}
B
Benjamin Pasero 已提交
275
		} catch (err) {
276 277 278 279 280
			// TODO: Do something with err if this is not the shutdown case
		}

		// clean up subscriptions
		try {
J
Joao Moreno 已提交
281
			dispose(extension.subscriptions);
B
Benjamin Pasero 已提交
282
		} catch (err) {
283 284
			// TODO: Do something with err if this is not the shutdown case
		}
285 286

		return result;
287
	}
E
Erich Gamma 已提交
288

A
Alex Dima 已提交
289
	// --- impl
A
Alex Dima 已提交
290

291
	private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
292
		this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
A
Alex Dima 已提交
293
		return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
294
			const activationTimes = activatedExtension.activationTimes;
295
			const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
296
			this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
297
			this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
A
Alex Dima 已提交
298 299
			return activatedExtension;
		}, (err) => {
300
			this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
A
Alex Dima 已提交
301 302
			throw err;
		});
E
Erich Gamma 已提交
303 304
	}

305
	private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
306
		const event = getTelemetryActivationEvent(extensionDescription, reason);
307 308 309 310 311 312 313 314 315
		/* __GDPR__
			"extensionActivationTimes" : {
				"${include}": [
					"${TelemetryActivationEvent}",
					"${ExtensionActivationTimes}"
				],
				"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
			}
		*/
A
Alex Dima 已提交
316
		this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', {
317 318 319 320 321 322
			...event,
			...(activationTimes || {}),
			outcome,
		});
	}

323
	private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
324
		const event = getTelemetryActivationEvent(extensionDescription, reason);
K
kieferrm 已提交
325
		/* __GDPR__
K
kieferrm 已提交
326 327 328 329 330 331
			"activatePlugin" : {
				"${include}": [
					"${TelemetryActivationEvent}"
				]
			}
		*/
A
Alex Dima 已提交
332
		this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event);
A
Alex Dima 已提交
333 334
		if (!extensionDescription.main) {
			// Treat the extension as being empty => NOT AN ERROR CASE
335
			return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
A
Alex Dima 已提交
336
		}
337

338
		this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`);
339

A
Alex Dima 已提交
340
		const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
341
		return Promise.all<any>([
342
			loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
A
Alex Dima 已提交
343 344
			this._loadExtensionContext(extensionDescription)
		]).then(values => {
345
			return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
A
Alex Dima 已提交
346
		});
E
Erich Gamma 已提交
347 348
	}

349
	private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
E
Erich Gamma 已提交
350

351 352
		const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
		const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
E
Erich Gamma 已提交
353

354
		this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
355
		return Promise.all([
356
			globalState.whenReady,
357 358
			workspaceState.whenReady,
			this._storagePath.whenReady
359
		]).then(() => {
360
			const that = this;
A
Alex Dima 已提交
361
			return Object.freeze(<IExtensionContext>{
E
Erich Gamma 已提交
362 363 364
				globalState,
				workspaceState,
				subscriptions: [],
365
				get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
366
				storagePath: this._storagePath.workspaceValue(extensionDescription),
S
Sandeep Somavarapu 已提交
367
				globalStoragePath: this._storagePath.globalValue(extensionDescription),
A
Alex Dima 已提交
368
				asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
369
				logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier),
370
				executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local,
E
Erich Gamma 已提交
371 372 373 374
			});
		});
	}

375
	private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
A
Alex Dima 已提交
376 377
		// Make sure the extension's surface is not undefined
		extensionModule = extensionModule || {
378 379 380 381
			activate: undefined,
			deactivate: undefined
		};

382
		return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
383
			return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
384 385 386
		});
	}

387
	private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
A
Alex Dima 已提交
388
		if (typeof extensionModule.activate === 'function') {
389
			try {
390
				activationTimesBuilder.activateCallStart();
391
				logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
J
Johannes Rieken 已提交
392
				const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
393 394 395
				activationTimesBuilder.activateCallStop();

				activationTimesBuilder.activateResolveStart();
396
				return Promise.resolve(activateResult).then((value) => {
397 398 399
					activationTimesBuilder.activateResolveStop();
					return value;
				});
400
			} catch (err) {
401
				return Promise.reject(err);
402 403
			}
		} else {
A
Alex Dima 已提交
404
			// No activate found => the module is the extension's exports
405
			return Promise.resolve<IExtensionAPI>(extensionModule);
406 407 408
		}
	}

A
Alex Dima 已提交
409 410 411
	// -- eager activation

	// Handle "eager" activation extensions
412
	private _handleEagerExtensions(): Promise<void> {
R
Rob Lourens 已提交
413
		this._activateByEvent('*', true).then(undefined, (err) => {
A
Alex Dima 已提交
414 415 416
			console.error(err);
		});

417
		return this._handleWorkspaceContainsEagerExtensions(this._extHostWorkspace.workspace);
A
Alex Dima 已提交
418 419
	}

M
Matt Bierner 已提交
420
	private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspace | undefined): Promise<void> {
A
Alex Dima 已提交
421
		if (!workspace || workspace.folders.length === 0) {
R
Rob Lourens 已提交
422
			return Promise.resolve(undefined);
A
Alex Dima 已提交
423 424 425 426 427 428 429 430 431
		}

		return Promise.all(
			this._registry.getAllExtensionDescriptions().map((desc) => {
				return this._handleWorkspaceContainsEagerExtension(workspace, desc);
			})
		).then(() => { });
	}

432
	private _handleWorkspaceContainsEagerExtension(workspace: IWorkspace, desc: IExtensionDescription): Promise<void> {
A
Alex Dima 已提交
433 434
		const activationEvents = desc.activationEvents;
		if (!activationEvents) {
R
Rob Lourens 已提交
435
			return Promise.resolve(undefined);
A
Alex Dima 已提交
436 437 438 439 440
		}

		const fileNames: string[] = [];
		const globPatterns: string[] = [];

441 442 443
		for (const activationEvent of activationEvents) {
			if (/^workspaceContains:/.test(activationEvent)) {
				const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
A
Alex Dima 已提交
444 445 446 447 448 449 450 451 452
				if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
					globPatterns.push(fileNameOrGlob);
				} else {
					fileNames.push(fileNameOrGlob);
				}
			}
		}

		if (fileNames.length === 0 && globPatterns.length === 0) {
R
Rob Lourens 已提交
453
			return Promise.resolve(undefined);
A
Alex Dima 已提交
454 455
		}

456 457
		const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { });
		const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns);
A
Alex Dima 已提交
458 459 460 461

		return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
	}

462
	private async _activateIfFileName(workspace: IWorkspace, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
A
Alex Dima 已提交
463 464 465

		// find exact path
		for (const { uri } of workspace.folders) {
J
Johannes Rieken 已提交
466
			if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) {
A
Alex Dima 已提交
467 468 469
				// the file was found
				return (
					this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
R
Rob Lourens 已提交
470
						.then(undefined, err => console.error(err))
A
Alex Dima 已提交
471 472 473 474 475 476 477
				);
			}
		}

		return undefined;
	}

478
	private async _activateIfGlobPatterns(extensionId: ExtensionIdentifier, globPatterns: string[]): Promise<void> {
479
		this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`);
A
Alex Dima 已提交
480 481

		if (globPatterns.length === 0) {
R
Rob Lourens 已提交
482
			return Promise.resolve(undefined);
A
Alex Dima 已提交
483 484 485 486 487 488 489 490
		}

		const tokenSource = new CancellationTokenSource();
		const searchP = this._mainThreadWorkspaceProxy.$checkExists(globPatterns, tokenSource.token);

		const timer = setTimeout(async () => {
			tokenSource.cancel();
			this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`))
R
Rob Lourens 已提交
491
				.then(undefined, err => console.error(err));
A
Alex Dima 已提交
492 493
		}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);

M
Matt Bierner 已提交
494
		let exists: boolean = false;
A
Alex Dima 已提交
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
		try {
			exists = await searchP;
		} catch (err) {
			if (!errors.isPromiseCanceledError(err)) {
				console.error(err);
			}
		}

		tokenSource.dispose();
		clearTimeout(timer);

		if (exists) {
			// a file was found matching one of the glob patterns
			return (
				this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`))
R
Rob Lourens 已提交
510
					.then(undefined, err => console.error(err))
A
Alex Dima 已提交
511 512 513
			);
		}

R
Rob Lourens 已提交
514
		return Promise.resolve(undefined);
A
Alex Dima 已提交
515 516 517
	}

	private _handleExtensionTests(): Promise<void> {
518 519 520 521 522 523 524 525
		return this._doHandleExtensionTests().then(undefined, error => {
			console.error(error); // ensure any error message makes it onto the console

			return Promise.reject(error);
		});
	}

	private _doHandleExtensionTests(): Promise<void> {
526
		const { extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
527
		if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
R
Rob Lourens 已提交
528
			return Promise.resolve(undefined);
A
Alex Dima 已提交
529 530
		}

531
		const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
B
Benjamin Pasero 已提交
532

A
Alex Dima 已提交
533
		// Require the test runner via node require from the provided path
P
Pine Wu 已提交
534
		let testRunner: ITestRunner | INewTestRunner | undefined;
M
Matt Bierner 已提交
535
		let requireError: Error | undefined;
A
Alex Dima 已提交
536
		try {
537
			testRunner = <any>require.__$__nodeRequire(extensionTestsPath);
A
Alex Dima 已提交
538 539 540 541
		} catch (error) {
			requireError = error;
		}

P
Pine Wu 已提交
542
		// Execute the runner if it follows the old `run` spec
A
Alex Dima 已提交
543 544
		if (testRunner && typeof testRunner.run === 'function') {
			return new Promise<void>((c, e) => {
P
Pine Wu 已提交
545
				const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
A
Alex Dima 已提交
546 547 548
					if (error) {
						e(error.toString());
					} else {
R
Rob Lourens 已提交
549
						c(undefined);
A
Alex Dima 已提交
550 551 552
					}

					// after tests have run, we shutdown the host
553
					this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
P
Pine Wu 已提交
554 555 556 557 558 559 560 561 562 563 564
				};

				const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);

				// Using the new API `run(): Promise<void>`
				if (runResult && runResult.then) {
					runResult
						.then(() => {
							c();
							this._gracefulExit(0);
						})
P
Pine Wu 已提交
565 566
						.catch((err: Error) => {
							e(err.toString());
P
Pine Wu 已提交
567 568 569
							this._gracefulExit(1);
						});
				}
A
Alex Dima 已提交
570 571 572 573 574 575 576 577
			});
		}

		// Otherwise make sure to shutdown anyway even in case of an error
		else {
			this._gracefulExit(1 /* ERROR */);
		}

578
		return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
A
Alex Dima 已提交
579 580 581 582 583
	}

	private _gracefulExit(code: number): void {
		// to give the PH process a chance to flush any outstanding console
		// messages to the main process, we delay the exit() by some time
A
Alex Dima 已提交
584
		setTimeout(() => {
585
			// If extension tests are running, give the exit code to the renderer
586
			if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
A
Alex Dima 已提交
587 588 589
				this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
				return;
			}
590

J
Johannes Rieken 已提交
591
			this._hostUtils.exit(code);
A
Alex Dima 已提交
592
		}, 500);
A
Alex Dima 已提交
593 594
	}

J
Johannes Rieken 已提交
595
	private _startExtensionHost(): Promise<void> {
A
Alex Dima 已提交
596 597 598 599 600
		if (this._started) {
			throw new Error(`Extension host is already started!`);
		}
		this._started = true;

S
Sandeep Somavarapu 已提交
601 602
		return this._readyToStartExtensionHost.wait()
			.then(() => this._readyToRunExtensions.open())
603
			.then(() => this._handleEagerExtensions())
A
Alex Dima 已提交
604 605 606 607 608 609
			.then(() => this._handleExtensionTests())
			.then(() => {
				this._extHostLogService.info(`eager extensions activated`);
			});
	}

A
Alex Dima 已提交
610 611 612 613 614
	// -- called by extensions

	public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
		this._resolvers[authorityPrefix] = resolver;
		return toDisposable(() => {
A
Alex Dima 已提交
615
			delete this._resolvers[authorityPrefix];
A
Alex Dima 已提交
616 617 618
		});
	}

E
Erich Gamma 已提交
619 620
	// -- called by main thread

A
Tweaks  
Alex Dima 已提交
621
	public async $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
A
Alex Dima 已提交
622 623 624 625 626 627
		const authorityPlusIndex = remoteAuthority.indexOf('+');
		if (authorityPlusIndex === -1) {
			throw new Error(`Not an authority that can be resolved!`);
		}
		const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);

A
Alex Dima 已提交
628
		await this._almostReadyToRunExtensions.wait();
A
Alex Dima 已提交
629 630 631 632
		await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);

		const resolver = this._resolvers[authorityPrefix];
		if (!resolver) {
633
			throw new Error(`No remote extension installed to resolve ${authorityPrefix}.`);
A
Alex Dima 已提交
634 635
		}

A
Tweaks  
Alex Dima 已提交
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
		try {
			const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
			return {
				type: 'ok',
				value: {
					authority: remoteAuthority,
					host: result.host,
					port: result.port,
				}
			};
		} catch (err) {
			if (err instanceof RemoteAuthorityResolverError) {
				return {
					type: 'error',
					error: {
						code: err._code,
						message: err._message,
						detail: err._detail
					}
				};
			}
			throw err;
		}
A
Alex Dima 已提交
659 660
	}

661
	public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
A
Alex Dima 已提交
662 663 664 665
		this._registry.keepOnly(enabledExtensionIds);
		return this._startExtensionHost();
	}

J
Johannes Rieken 已提交
666
	public $activateByEvent(activationEvent: string): Promise<void> {
A
Alex Dima 已提交
667
		return (
A
Alex Dima 已提交
668
			this._readyToRunExtensions.wait()
A
Alex Dima 已提交
669 670
				.then(_ => this._activateByEvent(activationEvent, false))
		);
671
	}
672

673
	public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
A
Alex Dima 已提交
674
		await this._readyToRunExtensions.wait();
675 676 677 678 679 680
		if (!this._registry.getExtensionDescription(extensionId)) {
			// unknown extension => ignore
			return false;
		}
		await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent));
		return true;
681 682
	}

683
	public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
684
		toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
685 686 687 688 689 690 691 692

		const trie = await this.getExtensionPathIndex();

		await Promise.all(toRemove.map(async (extensionId) => {
			const extensionDescription = this._registry.getExtensionDescription(extensionId);
			if (!extensionDescription) {
				return;
			}
J
Johannes Rieken 已提交
693
			const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
B
Benjamin Pasero 已提交
694
			trie.delete(URI.file(realpathValue).fsPath);
695 696 697
		}));

		await Promise.all(toAdd.map(async (extensionDescription) => {
J
Johannes Rieken 已提交
698
			const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath);
B
Benjamin Pasero 已提交
699
			trie.set(URI.file(realpathValue).fsPath, extensionDescription);
700 701
		}));

702
		this._registry.deltaExtensions(toAdd, toRemove);
703 704 705
		return Promise.resolve(undefined);
	}

706 707 708 709
	public async $test_latency(n: number): Promise<number> {
		return n;
	}

710 711
	public async $test_up(b: VSBuffer): Promise<number> {
		return b.byteLength;
712 713
	}

714
	public async $test_down(size: number): Promise<VSBuffer> {
J
Johannes Rieken 已提交
715 716 717
		let buff = VSBuffer.alloc(size);
		let value = Math.random() % 256;
		for (let i = 0; i < size; i++) {
718
			buff.writeUInt8(value, i);
J
Johannes Rieken 已提交
719 720
		}
		return buff;
721 722
	}

723 724
}

725
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
726
	let r: T | null = null;
727
	activationTimesBuilder.codeLoadingStart();
728
	logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
E
Erich Gamma 已提交
729 730
	try {
		r = require.__$__nodeRequire<T>(modulePath);
B
Benjamin Pasero 已提交
731
	} catch (e) {
732
		return Promise.reject(e);
733 734
	} finally {
		activationTimesBuilder.codeLoadingStop();
E
Erich Gamma 已提交
735
	}
736
	return Promise.resolve(r);
E
Erich Gamma 已提交
737
}
A
Alex Dima 已提交
738

R
Rob Lourens 已提交
739 740 741 742 743
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
	const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
		reason instanceof ExtensionActivatedByAPI ? 'api' :
			'';

K
kieferrm 已提交
744
	/* __GDPR__FRAGMENT__
K
kieferrm 已提交
745 746 747
		"TelemetryActivationEvent" : {
			"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
			"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
C
Christof Marti 已提交
748
			"extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
749
			"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
K
kieferrm 已提交
750
			"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
R
Rob Lourens 已提交
751 752
			"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
			"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
K
kieferrm 已提交
753 754
		}
	*/
755
	const event = {
756
		id: extensionDescription.identifier.value,
A
Alex Dima 已提交
757
		name: extensionDescription.name,
C
Christof Marti 已提交
758
		extensionVersion: extensionDescription.version,
759
		publisherDisplayName: extensionDescription.publisher,
760
		activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
R
Rob Lourens 已提交
761 762
		isBuiltin: extensionDescription.isBuiltin,
		reason: reasonStr
A
Alex Dima 已提交
763 764 765
	};

	return event;
766
}