extHostExtensionService.ts 30.4 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';
A
Alex Dima 已提交
10
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
A
Alex Dima 已提交
11 12
import { TernarySearchTree } from 'vs/base/common/map';
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
import { createApiFactory, IExtensionApiFactory, NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory } from 'vs/workbench/api/node/extHost.api.impl';
16
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol';
17
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
18
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule, HostExtension } from 'vs/workbench/api/node/extHostExtensionActivator';
19
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
A
Alex Dima 已提交
20
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
21
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
22
import { ExtensionActivationError } 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/services/extensions/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';
A
Alex Dima 已提交
28
import * as vscode from 'vscode';
29
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
30
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
31
import { Schemas } from 'vs/base/common/network';
M
Matt Bierner 已提交
32
import { withNullAsUndefined } from 'vs/base/common/types';
B
Benjamin Pasero 已提交
33
import { realpath } from 'vs/base/node/extpath';
A
Alex Dima 已提交
34

A
Alex Dima 已提交
35
class ExtensionMemento implements IExtensionMemento {
A
Alex Dima 已提交
36

37 38 39
	private readonly _id: string;
	private readonly _shared: boolean;
	private readonly _storage: ExtHostStorage;
A
Alex Dima 已提交
40

J
Johannes Rieken 已提交
41
	private readonly _init: Promise<ExtensionMemento>;
A
Alex Dima 已提交
42
	private _value: { [n: string]: any; };
43
	private readonly _storageListener: IDisposable;
A
Alex Dima 已提交
44

A
Alex Dima 已提交
45
	constructor(id: string, global: boolean, storage: ExtHostStorage) {
A
Alex Dima 已提交
46 47 48 49 50 51 52 53
		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;
		});
54 55 56 57 58 59

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

J
Johannes Rieken 已提交
62
	get whenReady(): Promise<ExtensionMemento> {
A
Alex Dima 已提交
63 64 65 66 67 68 69 70 71 72 73
		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 已提交
74
	update(key: string, value: any): Promise<boolean> {
A
Alex Dima 已提交
75 76 77 78 79
		this._value[key] = value;
		return this._storage
			.setValue(this._shared, this._id, this._value)
			.then(() => true);
	}
80 81 82 83

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

86 87
class ExtensionStoragePath {

M
Matt Bierner 已提交
88
	private readonly _workspace?: IStaticWorkspaceData;
89 90
	private readonly _environment: IEnvironment;

M
Matt Bierner 已提交
91 92
	private readonly _ready: Promise<string | undefined>;
	private _value?: string;
93

M
Matt Bierner 已提交
94
	constructor(workspace: IStaticWorkspaceData | undefined, environment: IEnvironment) {
95 96 97 98 99 100 101 102 103
		this._workspace = workspace;
		this._environment = environment;
		this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
	}

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

M
Matt Bierner 已提交
104
	workspaceValue(extension: IExtensionDescription): string | undefined {
105
		if (this._value) {
106
			return path.join(this._value, extension.identifier.value);
107 108 109 110
		}
		return undefined;
	}

111
	globalValue(extension: IExtensionDescription): string {
S
Sandeep Somavarapu 已提交
112
		return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value.toLowerCase());
113 114
	}

M
Matt Bierner 已提交
115
	private async _getOrCreateWorkspaceStoragePath(): Promise<string | undefined> {
116 117 118 119
		if (!this._workspace) {
			return Promise.resolve(undefined);
		}

120 121 122
		if (!this._environment.appSettingsHome) {
			return undefined;
		}
123
		const storageName = this._workspace.id;
A
Alex Dima 已提交
124
		const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
125

A
Alex Dima 已提交
126
		const exists = await pfs.dirExists(storagePath);
127 128 129 130 131 132

		if (exists) {
			return storagePath;
		}

		try {
A
Alex Dima 已提交
133 134 135
			await pfs.mkdirp(storagePath);
			await pfs.writeFile(
				path.join(storagePath, 'meta.json'),
136 137 138 139 140 141 142 143 144 145 146 147 148 149
				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 已提交
150 151 152 153 154

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

A
Alex Dima 已提交
155
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
A
Alex Dima 已提交
156

A
Alex Dima 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169
	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 已提交
170
	private readonly _almostReadyToRunExtensions: Barrier;
A
Alex Dima 已提交
171
	private readonly _readyToRunExtensions: Barrier;
A
Alex Dima 已提交
172 173
	private readonly _registry: ExtensionDescriptionRegistry;
	private readonly _storage: ExtHostStorage;
174
	private readonly _storagePath: ExtensionStoragePath;
A
Alex Dima 已提交
175
	private readonly _activator: ExtensionsActivator;
A
Alex Dima 已提交
176
	private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>> | null;
A
Alex Dima 已提交
177 178
	private readonly _extensionApiFactory: IExtensionApiFactory;

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

A
Alex Dima 已提交
181 182 183 184 185
	private _started: boolean;

	constructor(
		nativeExit: (code?: number) => void,
		initData: IInitData,
186
		extHostContext: IMainContext,
187
		extHostWorkspace: ExtHostWorkspace,
J
Joao Moreno 已提交
188
		extHostConfiguration: ExtHostConfiguration,
A
Alex Dima 已提交
189
		extHostLogService: ExtHostLogService
190
	) {
A
Alex Dima 已提交
191 192 193 194 195 196 197 198 199 200 201
		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 已提交
202
		this._almostReadyToRunExtensions = new Barrier();
A
Alex Dima 已提交
203
		this._readyToRunExtensions = new Barrier();
A
Alex Dima 已提交
204
		this._registry = new ExtensionDescriptionRegistry(initData.extensions);
A
Alex Dima 已提交
205
		this._storage = new ExtHostStorage(this._extHostContext);
M
Matt Bierner 已提交
206
		this._storagePath = new ExtensionStoragePath(withNullAsUndefined(initData.workspace), initData.environment);
207 208 209 210 211

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

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

216 217
			actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
				if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
218
					const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
219 220 221
					await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent);
					return new HostExtension();
				}
A
Alex Dima 已提交
222
				const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
A
Alex Dima 已提交
223 224 225 226
				return this._activateExtension(extensionDescription, reason);
			}
		});
		this._extensionPathIndex = null;
227

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

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

A
Alex Dima 已提交
233 234
		this._started = false;

235
		this._initialize();
A
Alex Dima 已提交
236 237 238 239

		if (this._initData.autoStart) {
			this._startExtensionHost();
		}
240 241
	}

242 243 244
	private async _initialize(): Promise<void> {
		try {
			const configProvider = await this._extHostConfiguration.getConfigProvider();
245 246
			const extensionPaths = await this.getExtensionPathIndex();
			NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider));
A
Alex Dima 已提交
247
			NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar)));
248

249
			// Do this when extension service exists, but extensions are not being activated yet.
250
			await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy);
A
Alex Dima 已提交
251 252 253
			this._almostReadyToRunExtensions.open();

			await this._extHostWorkspace.waitForInitializeCall();
A
Alex Dima 已提交
254
			this._readyToRunExtensions.open();
255 256 257 258 259
		} catch (err) {
			errors.onUnexpectedError(err);
		}
	}

A
Alex Dima 已提交
260
	public async deactivateAll(): Promise<void> {
J
Johannes Rieken 已提交
261
		let allPromises: Promise<void>[] = [];
A
Alex Dima 已提交
262 263
		try {
			const allExtensions = this._registry.getAllExtensionDescriptions();
264
			const allExtensionsIds = allExtensions.map(ext => ext.identifier);
A
Alex Dima 已提交
265 266 267 268 269 270 271 272 273
			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;
274 275
	}

276
	public isActivated(extensionId: ExtensionIdentifier): boolean {
A
Alex Dima 已提交
277
		if (this._readyToRunExtensions.isOpen()) {
A
Alex Dima 已提交
278 279 280
			return this._activator.isActivated(extensionId);
		}
		return false;
281 282
	}

J
Johannes Rieken 已提交
283
	private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
A
Alex Dima 已提交
284
		const reason = new ExtensionActivatedByEvent(startup, activationEvent);
A
Alex Dima 已提交
285
		return this._activator.activateByEvent(activationEvent, reason);
286 287
	}

288
	private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
A
Alex Dima 已提交
289
		return this._activator.activateById(extensionId, reason);
290 291
	}

292
	public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
A
Alex Dima 已提交
293
		return this._activateById(extensionId, reason).then(() => {
294 295 296
			const extension = this._activator.getActivatedExtension(extensionId);
			if (extension.activationFailed) {
				// activation failed => bubble up the error as the promise result
297
				return Promise.reject(extension.activationFailedError);
298
			}
R
Rob Lourens 已提交
299
			return undefined;
300 301 302
		});
	}

A
Alex Dima 已提交
303
	public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
A
Alex Dima 已提交
304
		return this._readyToRunExtensions.wait().then(_ => this._registry);
305 306
	}

A
Alex Dima 已提交
307
	public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
A
Alex Dima 已提交
308
		if (this._readyToRunExtensions.isOpen()) {
A
Alex Dima 已提交
309 310 311
			return this._activator.getActivatedExtension(extensionId).exports;
		} else {
			return null;
E
Erich Gamma 已提交
312 313 314
		}
	}

315
	// create trie to enable fast 'filename -> extension id' look up
316
	public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
317
		if (!this._extensionPathIndex) {
318
			const tree = TernarySearchTree.forPaths<IExtensionDescription>();
A
Alex Dima 已提交
319
			const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
320 321 322
				if (!ext.main) {
					return undefined;
				}
B
Benjamin Pasero 已提交
323
				return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
324
			});
325
			this._extensionPathIndex = Promise.all(extensions).then(() => tree);
326 327 328 329
		}
		return this._extensionPathIndex;
	}

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

A
Alex Dima 已提交
333
		if (!this._readyToRunExtensions.isOpen()) {
A
Alex Dima 已提交
334 335 336 337
			return result;
		}

		if (!this._activator.isActivated(extensionId)) {
A
Alex Dima 已提交
338 339 340
			return result;
		}

341
		const extension = this._activator.getActivatedExtension(extensionId);
A
Alex Dima 已提交
342
		if (!extension) {
343
			return result;
344 345 346 347
		}

		// call deactivate if available
		try {
A
Alex Dima 已提交
348
			if (typeof extension.module.deactivate === 'function') {
R
Rob Lourens 已提交
349
				result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => {
350
					// TODO: Do something with err if this is not the shutdown case
R
Rob Lourens 已提交
351
					return Promise.resolve(undefined);
352
				});
353
			}
B
Benjamin Pasero 已提交
354
		} catch (err) {
355 356 357 358 359
			// TODO: Do something with err if this is not the shutdown case
		}

		// clean up subscriptions
		try {
J
Joao Moreno 已提交
360
			dispose(extension.subscriptions);
B
Benjamin Pasero 已提交
361
		} catch (err) {
362 363
			// TODO: Do something with err if this is not the shutdown case
		}
364 365

		return result;
366
	}
E
Erich Gamma 已提交
367

A
Alex Dima 已提交
368
	// --- impl
A
Alex Dima 已提交
369

370
	private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
371
		this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
A
Alex Dima 已提交
372
		return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
373
			const activationTimes = activatedExtension.activationTimes;
374
			const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
375
			this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
376
			this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
A
Alex Dima 已提交
377 378
			return activatedExtension;
		}, (err) => {
379
			this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
A
Alex Dima 已提交
380 381
			throw err;
		});
E
Erich Gamma 已提交
382 383
	}

384
	private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
385
		const event = getTelemetryActivationEvent(extensionDescription, reason);
386 387 388 389 390 391 392 393 394
		/* __GDPR__
			"extensionActivationTimes" : {
				"${include}": [
					"${TelemetryActivationEvent}",
					"${ExtensionActivationTimes}"
				],
				"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
			}
		*/
A
Alex Dima 已提交
395
		this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', {
396 397 398 399 400 401
			...event,
			...(activationTimes || {}),
			outcome,
		});
	}

402
	private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
403
		const event = getTelemetryActivationEvent(extensionDescription, reason);
K
kieferrm 已提交
404
		/* __GDPR__
K
kieferrm 已提交
405 406 407 408 409 410
			"activatePlugin" : {
				"${include}": [
					"${TelemetryActivationEvent}"
				]
			}
		*/
A
Alex Dima 已提交
411
		this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event);
A
Alex Dima 已提交
412 413
		if (!extensionDescription.main) {
			// Treat the extension as being empty => NOT AN ERROR CASE
414
			return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
A
Alex Dima 已提交
415
		}
416

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

A
Alex Dima 已提交
419
		const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
420
		return Promise.all<any>([
421
			loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
A
Alex Dima 已提交
422 423
			this._loadExtensionContext(extensionDescription)
		]).then(values => {
424
			return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
A
Alex Dima 已提交
425
		});
E
Erich Gamma 已提交
426 427
	}

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

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

433
		this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
434
		return Promise.all([
435
			globalState.whenReady,
436 437
			workspaceState.whenReady,
			this._storagePath.whenReady
438
		]).then(() => {
439
			const that = this;
A
Alex Dima 已提交
440
			return Object.freeze(<IExtensionContext>{
E
Erich Gamma 已提交
441 442 443
				globalState,
				workspaceState,
				subscriptions: [],
444
				get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
445
				storagePath: this._storagePath.workspaceValue(extensionDescription),
S
Sandeep Somavarapu 已提交
446
				globalStoragePath: this._storagePath.globalValue(extensionDescription),
A
Alex Dima 已提交
447
				asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
448
				logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier)
E
Erich Gamma 已提交
449 450 451 452
			});
		});
	}

453
	private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
A
Alex Dima 已提交
454 455
		// Make sure the extension's surface is not undefined
		extensionModule = extensionModule || {
456 457 458 459
			activate: undefined,
			deactivate: undefined
		};

460
		return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
461
			return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
462 463 464
		});
	}

465
	private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
A
Alex Dima 已提交
466
		if (typeof extensionModule.activate === 'function') {
467
			try {
468
				activationTimesBuilder.activateCallStart();
469
				logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`);
J
Johannes Rieken 已提交
470
				const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
471 472 473
				activationTimesBuilder.activateCallStop();

				activationTimesBuilder.activateResolveStart();
474
				return Promise.resolve(activateResult).then((value) => {
475 476 477
					activationTimesBuilder.activateResolveStop();
					return value;
				});
478
			} catch (err) {
479
				return Promise.reject(err);
480 481
			}
		} else {
A
Alex Dima 已提交
482
			// No activate found => the module is the extension's exports
483
			return Promise.resolve<IExtensionAPI>(extensionModule);
484 485 486
		}
	}

A
Alex Dima 已提交
487 488 489
	// -- eager activation

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

495
		return this._handleWorkspaceContainsEagerExtensions(this._extHostWorkspace.workspace);
A
Alex Dima 已提交
496 497
	}

M
Matt Bierner 已提交
498
	private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspace | undefined): Promise<void> {
A
Alex Dima 已提交
499
		if (!workspace || workspace.folders.length === 0) {
R
Rob Lourens 已提交
500
			return Promise.resolve(undefined);
A
Alex Dima 已提交
501 502 503 504 505 506 507 508 509
		}

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

510
	private _handleWorkspaceContainsEagerExtension(workspace: IWorkspace, desc: IExtensionDescription): Promise<void> {
A
Alex Dima 已提交
511 512
		const activationEvents = desc.activationEvents;
		if (!activationEvents) {
R
Rob Lourens 已提交
513
			return Promise.resolve(undefined);
A
Alex Dima 已提交
514 515 516 517 518
		}

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

519 520 521
		for (const activationEvent of activationEvents) {
			if (/^workspaceContains:/.test(activationEvent)) {
				const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length);
A
Alex Dima 已提交
522 523 524 525 526 527 528 529 530
				if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
					globPatterns.push(fileNameOrGlob);
				} else {
					fileNames.push(fileNameOrGlob);
				}
			}
		}

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

534 535
		const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { });
		const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns);
A
Alex Dima 已提交
536 537 538 539

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

540
	private async _activateIfFileName(workspace: IWorkspace, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
A
Alex Dima 已提交
541 542 543 544 545 546 547

		// 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}`))
R
Rob Lourens 已提交
548
						.then(undefined, err => console.error(err))
A
Alex Dima 已提交
549 550 551 552 553 554 555
				);
			}
		}

		return undefined;
	}

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

		if (globPatterns.length === 0) {
R
Rob Lourens 已提交
560
			return Promise.resolve(undefined);
A
Alex Dima 已提交
561 562 563 564 565 566 567 568
		}

		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 已提交
569
				.then(undefined, err => console.error(err));
A
Alex Dima 已提交
570 571
		}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);

M
Matt Bierner 已提交
572
		let exists: boolean = false;
A
Alex Dima 已提交
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
		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 已提交
588
					.then(undefined, err => console.error(err))
A
Alex Dima 已提交
589 590 591
			);
		}

R
Rob Lourens 已提交
592
		return Promise.resolve(undefined);
A
Alex Dima 已提交
593 594 595
	}

	private _handleExtensionTests(): Promise<void> {
596 597 598 599 600 601 602 603
		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> {
604 605
		const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
		if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
R
Rob Lourens 已提交
606
			return Promise.resolve(undefined);
A
Alex Dima 已提交
607 608
		}

609
		const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
B
Benjamin Pasero 已提交
610

A
Alex Dima 已提交
611
		// Require the test runner via node require from the provided path
M
Matt Bierner 已提交
612 613
		let testRunner: ITestRunner | undefined;
		let requireError: Error | undefined;
A
Alex Dima 已提交
614
		try {
615
			testRunner = <any>require.__$__nodeRequire(extensionTestsPath);
A
Alex Dima 已提交
616 617 618 619 620 621 622
		} catch (error) {
			requireError = error;
		}

		// Execute the runner if it follows our spec
		if (testRunner && typeof testRunner.run === 'function') {
			return new Promise<void>((c, e) => {
623
				testRunner!.run(extensionTestsPath, (error, failures) => {
A
Alex Dima 已提交
624 625 626
					if (error) {
						e(error.toString());
					} else {
R
Rob Lourens 已提交
627
						c(undefined);
A
Alex Dima 已提交
628 629 630
					}

					// after tests have run, we shutdown the host
631
					this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
A
Alex Dima 已提交
632 633 634 635 636 637 638 639 640
				});
			});
		}

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

641
		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 已提交
642 643 644 645 646
	}

	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 已提交
647
		setTimeout(() => {
648 649
			// If extension tests are running, give the exit code to the renderer
			if (this._initData.remoteAuthority && !!this._initData.environment.extensionTestsLocationURI) {
A
Alex Dima 已提交
650 651 652
				this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
				return;
			}
653

A
Alex Dima 已提交
654 655
			this._nativeExit(code);
		}, 500);
A
Alex Dima 已提交
656 657
	}

J
Johannes Rieken 已提交
658
	private _startExtensionHost(): Promise<void> {
A
Alex Dima 已提交
659 660 661 662 663
		if (this._started) {
			throw new Error(`Extension host is already started!`);
		}
		this._started = true;

A
Alex Dima 已提交
664
		return this._readyToRunExtensions.wait()
665
			.then(() => this._handleEagerExtensions())
A
Alex Dima 已提交
666 667 668 669 670 671
			.then(() => this._handleExtensionTests())
			.then(() => {
				this._extHostLogService.info(`eager extensions activated`);
			});
	}

A
Alex Dima 已提交
672 673 674 675 676
	// -- called by extensions

	public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
		this._resolvers[authorityPrefix] = resolver;
		return toDisposable(() => {
A
Alex Dima 已提交
677
			delete this._resolvers[authorityPrefix];
A
Alex Dima 已提交
678 679 680
		});
	}

E
Erich Gamma 已提交
681 682
	// -- called by main thread

A
Alex Dima 已提交
683
	public async $resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
A
Alex Dima 已提交
684 685 686 687 688 689
		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 已提交
690
		await this._almostReadyToRunExtensions.wait();
A
Alex Dima 已提交
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
		await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);

		const resolver = this._resolvers[authorityPrefix];
		if (!resolver) {
			throw new Error(`No resolver available for ${authorityPrefix}`);
		}

		const result = await resolver.resolve(remoteAuthority);
		return {
			authority: remoteAuthority,
			host: result.host,
			port: result.port,
			debugListenPort: result.debugListenPort,
			debugConnectPort: result.debugConnectPort,
		};
A
Alex Dima 已提交
706 707
	}

708
	public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
A
Alex Dima 已提交
709 710 711 712
		this._registry.keepOnly(enabledExtensionIds);
		return this._startExtensionHost();
	}

J
Johannes Rieken 已提交
713
	public $activateByEvent(activationEvent: string): Promise<void> {
A
Alex Dima 已提交
714
		return (
A
Alex Dima 已提交
715
			this._readyToRunExtensions.wait()
A
Alex Dima 已提交
716 717
				.then(_ => this._activateByEvent(activationEvent, false))
		);
718
	}
719

720
	public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
A
Alex Dima 已提交
721
		await this._readyToRunExtensions.wait();
722 723 724 725 726 727
		if (!this._registry.getExtensionDescription(extensionId)) {
			// unknown extension => ignore
			return false;
		}
		await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent));
		return true;
728 729
	}

730
	public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
731
		toAdd.forEach((extension) => (<any>extension).extensionLocation = URI.revive(extension.extensionLocation));
732 733 734 735 736 737 738 739

		const trie = await this.getExtensionPathIndex();

		await Promise.all(toRemove.map(async (extensionId) => {
			const extensionDescription = this._registry.getExtensionDescription(extensionId);
			if (!extensionDescription) {
				return;
			}
B
Benjamin Pasero 已提交
740 741
			const realpathValue = await realpath(extensionDescription.extensionLocation.fsPath);
			trie.delete(URI.file(realpathValue).fsPath);
742 743 744
		}));

		await Promise.all(toAdd.map(async (extensionDescription) => {
B
Benjamin Pasero 已提交
745 746
			const realpathValue = await realpath(extensionDescription.extensionLocation.fsPath);
			trie.set(URI.file(realpathValue).fsPath, extensionDescription);
747 748
		}));

749
		this._registry.deltaExtensions(toAdd, toRemove);
750 751 752
		return Promise.resolve(undefined);
	}

753 754 755 756 757 758 759 760 761
	public async $test_latency(n: number): Promise<number> {
		return n;
	}

	public async $test_up(b: Buffer): Promise<number> {
		return b.length;
	}

	public async $test_down(size: number): Promise<Buffer> {
762
		const b = Buffer.alloc(size, Math.random() % 256);
763 764 765
		return b;
	}

766 767
}

768
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
769
	let r: T | null = null;
770
	activationTimesBuilder.codeLoadingStart();
771
	logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
E
Erich Gamma 已提交
772 773
	try {
		r = require.__$__nodeRequire<T>(modulePath);
B
Benjamin Pasero 已提交
774
	} catch (e) {
775
		return Promise.reject(e);
776 777
	} finally {
		activationTimesBuilder.codeLoadingStop();
E
Erich Gamma 已提交
778
	}
779
	return Promise.resolve(r);
E
Erich Gamma 已提交
780
}
A
Alex Dima 已提交
781

R
Rob Lourens 已提交
782 783 784 785 786
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
	const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
		reason instanceof ExtensionActivatedByAPI ? 'api' :
			'';

K
kieferrm 已提交
787
	/* __GDPR__FRAGMENT__
K
kieferrm 已提交
788 789 790
		"TelemetryActivationEvent" : {
			"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
			"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
C
Christof Marti 已提交
791
			"extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
792
			"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
K
kieferrm 已提交
793
			"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
R
Rob Lourens 已提交
794 795
			"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
			"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
K
kieferrm 已提交
796 797
		}
	*/
798
	const event = {
799
		id: extensionDescription.identifier.value,
A
Alex Dima 已提交
800
		name: extensionDescription.name,
C
Christof Marti 已提交
801
		extensionVersion: extensionDescription.version,
802
		publisherDisplayName: extensionDescription.publisher,
803
		activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
R
Rob Lourens 已提交
804 805
		isBuiltin: extensionDescription.isBuiltin,
		reason: reasonStr
A
Alex Dima 已提交
806 807 808
	};

	return event;
809
}