extHostExtensionService.ts 25.7 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 7
import * as nls from 'vs/nls';
import * as path from 'path';
A
Alex Dima 已提交
8 9 10
import { Barrier } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
E
Erich Gamma 已提交
11
import Severity from 'vs/base/common/severity';
A
Alex Dima 已提交
12
import { URI } from 'vs/base/common/uri';
A
Alex Dima 已提交
13
import * as pfs from 'vs/base/node/pfs';
A
Alex Dima 已提交
14
import { ILogService } from 'vs/platform/log/common/log';
A
Alex Dima 已提交
15 16
import { createApiFactory, initializeExtensionApi, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, IWorkspaceData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol';
17
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
A
Alex Dima 已提交
18
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule } from 'vs/workbench/api/node/extHostExtensionActivator';
19
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
A
Alex Dima 已提交
20 21
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
22
import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
A
Alex Dima 已提交
23
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
24
import { connectProxyResolver } from 'vs/workbench/node/proxyResolver';
A
Alex Dima 已提交
25 26 27
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
28
import { CanonicalExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
A
Alex Dima 已提交
29

A
Alex Dima 已提交
30
class ExtensionMemento implements IExtensionMemento {
A
Alex Dima 已提交
31

32 33 34
	private readonly _id: string;
	private readonly _shared: boolean;
	private readonly _storage: ExtHostStorage;
A
Alex Dima 已提交
35

J
Johannes Rieken 已提交
36
	private readonly _init: Promise<ExtensionMemento>;
A
Alex Dima 已提交
37
	private _value: { [n: string]: any; };
38
	private readonly _storageListener: IDisposable;
A
Alex Dima 已提交
39

A
Alex Dima 已提交
40
	constructor(id: string, global: boolean, storage: ExtHostStorage) {
A
Alex Dima 已提交
41 42 43 44 45 46 47 48
		this._id = id;
		this._shared = global;
		this._storage = storage;

		this._init = this._storage.getValue(this._shared, this._id, Object.create(null)).then(value => {
			this._value = value;
			return this;
		});
49 50 51 52 53 54

		this._storageListener = this._storage.onDidChangeStorage(e => {
			if (e.shared === this._shared && e.key === this._id) {
				this._value = e.value;
			}
		});
A
Alex Dima 已提交
55 56
	}

J
Johannes Rieken 已提交
57
	get whenReady(): Promise<ExtensionMemento> {
A
Alex Dima 已提交
58 59 60 61 62 63 64 65 66 67 68
		return this._init;
	}

	get<T>(key: string, defaultValue: T): T {
		let value = this._value[key];
		if (typeof value === 'undefined') {
			value = defaultValue;
		}
		return value;
	}

J
Johannes Rieken 已提交
69
	update(key: string, value: any): Promise<boolean> {
A
Alex Dima 已提交
70 71 72 73 74
		this._value[key] = value;
		return this._storage
			.setValue(this._shared, this._id, this._value)
			.then(() => true);
	}
75 76 77 78

	dispose(): void {
		this._storageListener.dispose();
	}
A
Alex Dima 已提交
79 80
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
class ExtensionStoragePath {

	private readonly _workspace: IWorkspaceData;
	private readonly _environment: IEnvironment;

	private readonly _ready: Promise<string>;
	private _value: string;

	constructor(workspace: IWorkspaceData, environment: IEnvironment) {
		this._workspace = workspace;
		this._environment = environment;
		this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
	}

	get whenReady(): Promise<any> {
		return this._ready;
	}

99
	workspaceValue(extension: IExtensionDescription): string {
100
		if (this._value) {
101
			return path.join(this._value, extension.identifier.value);
102 103 104 105
		}
		return undefined;
	}

106
	globalValue(extension: IExtensionDescription): string {
107
		return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value);
108 109
	}

110 111 112 113 114 115
	private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
		if (!this._workspace) {
			return Promise.resolve(undefined);
		}

		const storageName = this._workspace.id;
A
Alex Dima 已提交
116
		const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
117

A
Alex Dima 已提交
118
		const exists = await pfs.dirExists(storagePath);
119 120 121 122 123 124

		if (exists) {
			return storagePath;
		}

		try {
A
Alex Dima 已提交
125 126 127
			await pfs.mkdirp(storagePath);
			await pfs.writeFile(
				path.join(storagePath, 'meta.json'),
128 129 130 131 132 133 134 135 136 137 138 139 140 141
				JSON.stringify({
					id: this._workspace.id,
					configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
					name: this._workspace.name
				}, undefined, 2)
			);
			return storagePath;

		} catch (e) {
			console.error(e);
			return undefined;
		}
	}
}
A
Alex Dima 已提交
142 143 144 145 146

interface ITestRunner {
	run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}

A
Alex Dima 已提交
147
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
A
Alex Dima 已提交
148

A
Alex Dima 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161
	private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;

	private readonly _nativeExit: (code?: number) => void;
	private readonly _initData: IInitData;
	private readonly _extHostContext: IMainContext;
	private readonly _extHostWorkspace: ExtHostWorkspace;
	private readonly _extHostConfiguration: ExtHostConfiguration;
	private readonly _extHostLogService: ExtHostLogService;

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

A
Alex Dima 已提交
162 163 164
	private readonly _barrier: Barrier;
	private readonly _registry: ExtensionDescriptionRegistry;
	private readonly _storage: ExtHostStorage;
165
	private readonly _storagePath: ExtensionStoragePath;
A
Alex Dima 已提交
166
	private readonly _activator: ExtensionsActivator;
167
	private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>>;
A
Alex Dima 已提交
168 169 170 171 172 173 174
	private readonly _extensionApiFactory: IExtensionApiFactory;

	private _started: boolean;

	constructor(
		nativeExit: (code?: number) => void,
		initData: IInitData,
175
		extHostContext: IMainContext,
176
		extHostWorkspace: ExtHostWorkspace,
J
Joao Moreno 已提交
177
		extHostConfiguration: ExtHostConfiguration,
A
Alex Dima 已提交
178
		extHostLogService: ExtHostLogService
179
	) {
A
Alex Dima 已提交
180 181 182 183 184 185 186 187 188 189 190
		this._nativeExit = nativeExit;
		this._initData = initData;
		this._extHostContext = extHostContext;
		this._extHostWorkspace = extHostWorkspace;
		this._extHostConfiguration = extHostConfiguration;
		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 已提交
191 192
		this._barrier = new Barrier();
		this._registry = new ExtensionDescriptionRegistry(initData.extensions);
A
Alex Dima 已提交
193
		this._storage = new ExtHostStorage(this._extHostContext);
194
		this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
A
Alex Dima 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
		this._activator = new ExtensionsActivator(this._registry, {
			showMessage: (severity: Severity, message: string): void => {
				this._mainThreadExtensionsProxy.$localShowMessage(severity, message);

				switch (severity) {
					case Severity.Error:
						console.error(message);
						break;
					case Severity.Warning:
						console.warn(message);
						break;
					default:
						console.log(message);
				}
			},

			actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
				return this._activateExtension(extensionDescription, reason);
			}
		});
		this._extensionPathIndex = null;
216

A
Alex Dima 已提交
217
		// initialize API first (i.e. do not release barrier until the API is initialized)
A
Alex Dima 已提交
218
		this._extensionApiFactory = createApiFactory(this._initData, this._extHostContext, this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._storage);
219

A
Alex Dima 已提交
220 221 222
		this._started = false;

		initializeExtensionApi(this, this._extensionApiFactory, this._registry).then(() => {
223
			// Do this when extension service exists, but extensions are not being activated yet.
A
Alex Dima 已提交
224
			return connectProxyResolver(this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._mainThreadTelemetryProxy);
225
		}).then(() => {
A
Alex Dima 已提交
226 227
			this._barrier.open();
		});
A
Alex Dima 已提交
228 229 230 231

		if (this._initData.autoStart) {
			this._startExtensionHost();
		}
232 233
	}

A
Alex Dima 已提交
234
	public async deactivateAll(): Promise<void> {
J
Johannes Rieken 已提交
235
		let allPromises: Promise<void>[] = [];
A
Alex Dima 已提交
236 237
		try {
			const allExtensions = this._registry.getAllExtensionDescriptions();
238
			const allExtensionsIds = allExtensions.map(ext => ext.identifier);
A
Alex Dima 已提交
239 240 241 242 243 244 245 246 247
			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;
248 249
	}

250
	public isActivated(extensionId: CanonicalExtensionIdentifier): boolean {
A
Alex Dima 已提交
251 252 253 254
		if (this._barrier.isOpen()) {
			return this._activator.isActivated(extensionId);
		}
		return false;
255 256
	}

J
Johannes Rieken 已提交
257
	private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
A
Alex Dima 已提交
258
		const reason = new ExtensionActivatedByEvent(startup, activationEvent);
A
Alex Dima 已提交
259
		return this._activator.activateByEvent(activationEvent, reason);
260 261
	}

262
	private _activateById(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
A
Alex Dima 已提交
263
		return this._activator.activateById(extensionId, reason);
264 265
	}

266
	public activateByIdWithErrors(extensionId: CanonicalExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
A
Alex Dima 已提交
267
		return this._activateById(extensionId, reason).then(() => {
268 269 270
			const extension = this._activator.getActivatedExtension(extensionId);
			if (extension.activationFailed) {
				// activation failed => bubble up the error as the promise result
271
				return Promise.reject(extension.activationFailedError);
272 273 274 275 276
			}
			return void 0;
		});
	}

A
Alex Dima 已提交
277 278
	public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
		return this._barrier.wait().then(_ => this._registry);
279 280
	}

281
	public getExtensionExports(extensionId: CanonicalExtensionIdentifier): IExtensionAPI {
A
Alex Dima 已提交
282 283 284 285
		if (this._barrier.isOpen()) {
			return this._activator.getActivatedExtension(extensionId).exports;
		} else {
			return null;
E
Erich Gamma 已提交
286 287 288
		}
	}

289
	// create trie to enable fast 'filename -> extension id' look up
290
	public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
291
		if (!this._extensionPathIndex) {
292
			const tree = TernarySearchTree.forPaths<IExtensionDescription>();
A
Alex Dima 已提交
293
			const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
294 295 296
				if (!ext.main) {
					return undefined;
				}
A
Alex Dima 已提交
297
				return pfs.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
298
			});
299
			this._extensionPathIndex = Promise.all(extensions).then(() => tree);
300 301 302 303
		}
		return this._extensionPathIndex;
	}

304
	private _deactivate(extensionId: CanonicalExtensionIdentifier): Promise<void> {
305
		let result = Promise.resolve(void 0);
306

A
Alex Dima 已提交
307 308 309 310 311
		if (!this._barrier.isOpen()) {
			return result;
		}

		if (!this._activator.isActivated(extensionId)) {
A
Alex Dima 已提交
312 313 314
			return result;
		}

A
Alex Dima 已提交
315
		let extension = this._activator.getActivatedExtension(extensionId);
A
Alex Dima 已提交
316
		if (!extension) {
317
			return result;
318 319 320 321
		}

		// call deactivate if available
		try {
A
Alex Dima 已提交
322
			if (typeof extension.module.deactivate === 'function') {
323
				result = Promise.resolve(extension.module.deactivate()).then(void 0, (err) => {
324
					// TODO: Do something with err if this is not the shutdown case
325
					return Promise.resolve(void 0);
326
				});
327
			}
B
Benjamin Pasero 已提交
328
		} catch (err) {
329 330 331 332 333
			// TODO: Do something with err if this is not the shutdown case
		}

		// clean up subscriptions
		try {
J
Joao Moreno 已提交
334
			dispose(extension.subscriptions);
B
Benjamin Pasero 已提交
335
		} catch (err) {
336 337
			// TODO: Do something with err if this is not the shutdown case
		}
338 339

		return result;
340
	}
E
Erich Gamma 已提交
341

342
	public addMessage(extensionId: CanonicalExtensionIdentifier, severity: Severity, message: string): void {
A
Alex Dima 已提交
343
		this._mainThreadExtensionsProxy.$addMessage(extensionId, severity, message);
A
Alex Dima 已提交
344 345
	}

A
Alex Dima 已提交
346
	// --- impl
A
Alex Dima 已提交
347

348
	private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
349
		this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
A
Alex Dima 已提交
350
		return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
351
			const activationTimes = activatedExtension.activationTimes;
A
Alex Dima 已提交
352
			let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
353
			this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
354
			this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
A
Alex Dima 已提交
355 356
			return activatedExtension;
		}, (err) => {
357
			this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.identifier);
358
			this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
A
Alex Dima 已提交
359 360
			throw err;
		});
E
Erich Gamma 已提交
361 362
	}

363 364 365 366 367 368 369 370 371 372 373
	private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
		let event = getTelemetryActivationEvent(extensionDescription, reason);
		/* __GDPR__
			"extensionActivationTimes" : {
				"${include}": [
					"${TelemetryActivationEvent}",
					"${ExtensionActivationTimes}"
				],
				"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
			}
		*/
A
Alex Dima 已提交
374
		this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', {
375 376 377 378 379 380
			...event,
			...(activationTimes || {}),
			outcome,
		});
	}

381
	private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
R
Rob Lourens 已提交
382
		let event = getTelemetryActivationEvent(extensionDescription, reason);
K
kieferrm 已提交
383
		/* __GDPR__
K
kieferrm 已提交
384 385 386 387 388 389
			"activatePlugin" : {
				"${include}": [
					"${TelemetryActivationEvent}"
				]
			}
		*/
A
Alex Dima 已提交
390
		this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event);
A
Alex Dima 已提交
391 392
		if (!extensionDescription.main) {
			// Treat the extension as being empty => NOT AN ERROR CASE
393
			return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
A
Alex Dima 已提交
394
		}
395

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

A
Alex Dima 已提交
398
		const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
399
		return Promise.all<any>([
400
			loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
A
Alex Dima 已提交
401 402
			this._loadExtensionContext(extensionDescription)
		]).then(values => {
403
			return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
A
Alex Dima 已提交
404
		});
E
Erich Gamma 已提交
405 406
	}

407
	private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<IExtensionContext> {
E
Erich Gamma 已提交
408

409 410
		let globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
		let workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
E
Erich Gamma 已提交
411

412
		this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
413
		return Promise.all([
414
			globalState.whenReady,
415 416
			workspaceState.whenReady,
			this._storagePath.whenReady
417
		]).then(() => {
418
			const that = this;
A
Alex Dima 已提交
419
			return Object.freeze(<IExtensionContext>{
E
Erich Gamma 已提交
420 421 422
				globalState,
				workspaceState,
				subscriptions: [],
423
				get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
424
				storagePath: this._storagePath.workspaceValue(extensionDescription),
425
				get globalStoragePath(): string { checkProposedApiEnabled(extensionDescription); return that._storagePath.globalValue(extensionDescription); },
A
Alex Dima 已提交
426
				asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
427
				logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier)
E
Erich Gamma 已提交
428 429 430 431
			});
		});
	}

432
	private static _callActivate(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
A
Alex Dima 已提交
433 434
		// Make sure the extension's surface is not undefined
		extensionModule = extensionModule || {
435 436 437 438
			activate: undefined,
			deactivate: undefined
		};

439
		return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
440
			return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
441 442 443
		});
	}

444
	private static _callActivateOptional(logService: ILogService, extensionId: CanonicalExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
A
Alex Dima 已提交
445
		if (typeof extensionModule.activate === 'function') {
446
			try {
447
				activationTimesBuilder.activateCallStart();
448
				logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
J
Johannes Rieken 已提交
449
				const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
450 451 452
				activationTimesBuilder.activateCallStop();

				activationTimesBuilder.activateResolveStart();
453
				return Promise.resolve(activateResult).then((value) => {
454 455 456
					activationTimesBuilder.activateResolveStop();
					return value;
				});
457
			} catch (err) {
458
				return Promise.reject(err);
459 460
			}
		} else {
A
Alex Dima 已提交
461
			// No activate found => the module is the extension's exports
462
			return Promise.resolve<IExtensionAPI>(extensionModule);
463 464 465
		}
	}

A
Alex Dima 已提交
466 467 468 469
	// -- eager activation

	// Handle "eager" activation extensions
	private _handleEagerExtensions(): Promise<void> {
470
		this._activateByEvent('*', true).then(void 0, (err) => {
A
Alex Dima 已提交
471 472 473 474 475 476 477 478
			console.error(err);
		});

		return this._handleWorkspaceContainsEagerExtensions(this._initData.workspace);
	}

	private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspaceData): Promise<void> {
		if (!workspace || workspace.folders.length === 0) {
479
			return Promise.resolve(void 0);
A
Alex Dima 已提交
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
		}

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

	private _handleWorkspaceContainsEagerExtension(workspace: IWorkspaceData, desc: IExtensionDescription): Promise<void> {
		const activationEvents = desc.activationEvents;
		if (!activationEvents) {
			return Promise.resolve(void 0);
		}

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

		for (let i = 0; i < activationEvents.length; i++) {
			if (/^workspaceContains:/.test(activationEvents[i])) {
				const fileNameOrGlob = activationEvents[i].substr('workspaceContains:'.length);
				if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
					globPatterns.push(fileNameOrGlob);
				} else {
					fileNames.push(fileNameOrGlob);
				}
			}
		}

		if (fileNames.length === 0 && globPatterns.length === 0) {
			return Promise.resolve(void 0);
		}

513 514
		const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { });
		const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns);
A
Alex Dima 已提交
515 516 517 518

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

519
	private async _activateIfFileName(workspace: IWorkspaceData, extensionId: CanonicalExtensionIdentifier, fileName: string): Promise<void> {
A
Alex Dima 已提交
520 521 522 523 524 525 526

		// find exact path
		for (const { uri } of workspace.folders) {
			if (await pfs.exists(path.join(URI.revive(uri).fsPath, fileName))) {
				// the file was found
				return (
					this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
527
						.then(void 0, err => console.error(err))
A
Alex Dima 已提交
528 529 530 531 532 533 534
				);
			}
		}

		return undefined;
	}

535 536
	private async _activateIfGlobPatterns(extensionId: CanonicalExtensionIdentifier, globPatterns: string[]): Promise<void> {
		this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`);
A
Alex Dima 已提交
537 538 539 540 541 542 543 544 545 546 547

		if (globPatterns.length === 0) {
			return Promise.resolve(void 0);
		}

		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(',')}`))
548
				.then(void 0, err => console.error(err));
A
Alex Dima 已提交
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
		}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);

		let exists: boolean;
		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(',')}`))
567
					.then(void 0, err => console.error(err))
A
Alex Dima 已提交
568 569 570 571 572 573 574 575
			);
		}

		return Promise.resolve(void 0);
	}

	private _handleExtensionTests(): Promise<void> {
		if (!this._initData.environment.extensionTestsPath || !this._initData.environment.extensionDevelopmentLocationURI) {
576
			return Promise.resolve(void 0);
A
Alex Dima 已提交
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
		}

		// Require the test runner via node require from the provided path
		let testRunner: ITestRunner;
		let requireError: Error;
		try {
			testRunner = <any>require.__$__nodeRequire(this._initData.environment.extensionTestsPath);
		} catch (error) {
			requireError = error;
		}

		// Execute the runner if it follows our spec
		if (testRunner && typeof testRunner.run === 'function') {
			return new Promise<void>((c, e) => {
				testRunner.run(this._initData.environment.extensionTestsPath, (error, failures) => {
					if (error) {
						e(error.toString());
					} else {
595
						c(void 0);
A
Alex Dima 已提交
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
					}

					// after tests have run, we shutdown the host
					this._gracefulExit(failures && failures > 0 ? 1 /* ERROR */ : 0 /* OK */);
				});
			});
		}

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

		return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._initData.environment.extensionTestsPath)));
	}

	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
		setTimeout(() => this._nativeExit(code), 500);
	}

J
Johannes Rieken 已提交
618
	private _startExtensionHost(): Promise<void> {
A
Alex Dima 已提交
619 620 621 622 623 624 625 626 627 628 629 630 631
		if (this._started) {
			throw new Error(`Extension host is already started!`);
		}
		this._started = true;

		return this._barrier.wait()
			.then(() => this._handleEagerExtensions())
			.then(() => this._handleExtensionTests())
			.then(() => {
				this._extHostLogService.info(`eager extensions activated`);
			});
	}

E
Erich Gamma 已提交
632 633
	// -- called by main thread

A
Alex Dima 已提交
634 635 636 637
	public async $resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
		throw new Error(`Not implemented`);
	}

638
	public $startExtensionHost(enabledExtensionIds: CanonicalExtensionIdentifier[]): Promise<void> {
A
Alex Dima 已提交
639 640 641 642
		this._registry.keepOnly(enabledExtensionIds);
		return this._startExtensionHost();
	}

J
Johannes Rieken 已提交
643
	public $activateByEvent(activationEvent: string): Promise<void> {
A
Alex Dima 已提交
644 645 646 647
		return (
			this._barrier.wait()
				.then(_ => this._activateByEvent(activationEvent, false))
		);
648 649 650
	}
}

651
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
652
	let r: T | null = null;
653
	activationTimesBuilder.codeLoadingStart();
654
	logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
E
Erich Gamma 已提交
655 656
	try {
		r = require.__$__nodeRequire<T>(modulePath);
B
Benjamin Pasero 已提交
657
	} catch (e) {
658
		return Promise.reject(e);
659 660
	} finally {
		activationTimesBuilder.codeLoadingStop();
E
Erich Gamma 已提交
661
	}
662
	return Promise.resolve(r);
E
Erich Gamma 已提交
663
}
A
Alex Dima 已提交
664

R
Rob Lourens 已提交
665 666 667 668 669
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
	const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
		reason instanceof ExtensionActivatedByAPI ? 'api' :
			'';

K
kieferrm 已提交
670
	/* __GDPR__FRAGMENT__
K
kieferrm 已提交
671 672 673
		"TelemetryActivationEvent" : {
			"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
			"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
C
Christof Marti 已提交
674
			"extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
675
			"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
K
kieferrm 已提交
676
			"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
R
Rob Lourens 已提交
677 678
			"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
			"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
K
kieferrm 已提交
679 680
		}
	*/
A
Alex Dima 已提交
681
	let event = {
682
		id: extensionDescription.identifier.value,
A
Alex Dima 已提交
683
		name: extensionDescription.name,
C
Christof Marti 已提交
684
		extensionVersion: extensionDescription.version,
685
		publisherDisplayName: extensionDescription.publisher,
686
		activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
R
Rob Lourens 已提交
687 688
		isBuiltin: extensionDescription.isBuiltin,
		reason: reasonStr
A
Alex Dima 已提交
689 690 691
	};

	return event;
692
}