|
|
|
|
@@ -17,8 +17,8 @@ import * as platform from 'vs/base/common/platform';
|
|
|
|
|
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
|
|
|
|
|
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
|
|
|
|
|
import { USER_MANIFEST_CACHE_FILE, BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions';
|
|
|
|
|
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
|
|
|
|
|
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
|
|
|
|
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
|
|
|
|
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
|
|
|
|
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
|
|
|
|
import { ExtensionScanner, ILog, ExtensionScannerInput, IExtensionResolver, IExtensionReference, Translations } from 'vs/workbench/services/extensions/node/extensionPoints';
|
|
|
|
|
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
|
|
|
|
|
@@ -35,13 +35,13 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
|
|
|
|
import { mark, time } from 'vs/base/common/performance';
|
|
|
|
|
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
|
|
|
|
import { Barrier } from 'vs/base/common/async';
|
|
|
|
|
import Event, { Emitter } from 'vs/base/common/event';
|
|
|
|
|
import { Event, Emitter } from 'vs/base/common/event';
|
|
|
|
|
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
|
|
|
|
|
import product from 'vs/platform/node/product';
|
|
|
|
|
import * as strings from 'vs/base/common/strings';
|
|
|
|
|
import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
|
|
|
|
|
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
|
|
|
|
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
|
|
|
|
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
|
|
|
|
|
|
|
|
|
let _SystemExtensionsRoot: string = null;
|
|
|
|
|
function getSystemExtensionsRoot(): string {
|
|
|
|
|
@@ -111,10 +111,160 @@ function messageWithSource2(source: string, message: string): string {
|
|
|
|
|
const hasOwnProperty = Object.hasOwnProperty;
|
|
|
|
|
const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
|
|
|
|
|
|
|
|
|
|
export class ExtensionHostProcessManager extends Disposable {
|
|
|
|
|
|
|
|
|
|
public readonly onDidCrash: Event<[number, string]>;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
|
|
|
|
|
*/
|
|
|
|
|
private readonly _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
|
|
|
|
|
private readonly _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
|
|
|
|
|
private readonly _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
|
|
|
|
|
private _extensionHostProcessRPCProtocol: RPCProtocol;
|
|
|
|
|
private readonly _extensionHostProcessCustomers: IDisposable[];
|
|
|
|
|
private readonly _extensionHostProcessWorker: ExtensionHostProcessWorker;
|
|
|
|
|
/**
|
|
|
|
|
* winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object.
|
|
|
|
|
*/
|
|
|
|
|
private readonly _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>;
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
extensionHostProcessWorker: ExtensionHostProcessWorker,
|
|
|
|
|
initialActivationEvents: string[],
|
|
|
|
|
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
|
|
|
|
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
|
|
|
|
) {
|
|
|
|
|
super();
|
|
|
|
|
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
|
|
|
|
|
this._extensionHostProcessActivationTimes = Object.create(null);
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors = Object.create(null);
|
|
|
|
|
this._extensionHostProcessRPCProtocol = null;
|
|
|
|
|
this._extensionHostProcessCustomers = [];
|
|
|
|
|
this._extensionHostProcessProxy = null;
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessWorker = extensionHostProcessWorker;
|
|
|
|
|
this.onDidCrash = this._extensionHostProcessWorker.onCrashed;
|
|
|
|
|
this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then(
|
|
|
|
|
(protocol) => {
|
|
|
|
|
return { value: this._createExtensionHostCustomers(protocol) };
|
|
|
|
|
},
|
|
|
|
|
(err) => {
|
|
|
|
|
console.error('Error received from starting extension host');
|
|
|
|
|
console.error(err);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
this._extensionHostProcessProxy.then(() => {
|
|
|
|
|
initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public dispose(): void {
|
|
|
|
|
if (this._extensionHostProcessWorker) {
|
|
|
|
|
this._extensionHostProcessWorker.dispose();
|
|
|
|
|
}
|
|
|
|
|
if (this._extensionHostProcessRPCProtocol) {
|
|
|
|
|
this._extensionHostProcessRPCProtocol.dispose();
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) {
|
|
|
|
|
const customer = this._extensionHostProcessCustomers[i];
|
|
|
|
|
try {
|
|
|
|
|
customer.dispose();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
errors.onUnexpectedError(err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getActivatedExtensionIds(): string[] {
|
|
|
|
|
return Object.keys(this._extensionHostProcessActivationTimes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getActivationTimes(): { [id: string]: ActivationTimes; } {
|
|
|
|
|
return this._extensionHostProcessActivationTimes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getRuntimeErrors(): { [id: string]: Error[]; } {
|
|
|
|
|
return this._extensionHostExtensionRuntimeErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public canProfileExtensionHost(): boolean {
|
|
|
|
|
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
|
|
|
|
|
|
|
|
|
|
if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) {
|
|
|
|
|
protocol = asLoggingProtocol(protocol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol);
|
|
|
|
|
const extHostContext: IExtHostContext = this._extensionHostProcessRPCProtocol;
|
|
|
|
|
|
|
|
|
|
// Named customers
|
|
|
|
|
const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
|
|
|
|
|
for (let i = 0, len = namedCustomers.length; i < len; i++) {
|
|
|
|
|
const [id, ctor] = namedCustomers[i];
|
|
|
|
|
const instance = this._instantiationService.createInstance(ctor, extHostContext);
|
|
|
|
|
this._extensionHostProcessCustomers.push(instance);
|
|
|
|
|
this._extensionHostProcessRPCProtocol.set(id, instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Customers
|
|
|
|
|
const customers = ExtHostCustomersRegistry.getCustomers();
|
|
|
|
|
for (let i = 0, len = customers.length; i < len; i++) {
|
|
|
|
|
const ctor = customers[i];
|
|
|
|
|
const instance = this._instantiationService.createInstance(ctor, extHostContext);
|
|
|
|
|
this._extensionHostProcessCustomers.push(instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that no named customers are missing
|
|
|
|
|
const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => MainContext[key]);
|
|
|
|
|
this._extensionHostProcessRPCProtocol.assertRegistered(expected);
|
|
|
|
|
|
|
|
|
|
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public activateByEvent(activationEvent: string): TPromise<void> {
|
|
|
|
|
if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) {
|
|
|
|
|
return NO_OP_VOID_PROMISE;
|
|
|
|
|
}
|
|
|
|
|
return this._extensionHostProcessProxy.then((proxy) => {
|
|
|
|
|
return proxy.value.$activateByEvent(activationEvent);
|
|
|
|
|
}).then(() => {
|
|
|
|
|
this._extensionHostProcessFinishedActivateEvents[activationEvent] = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public startExtensionHostProfile(): TPromise<ProfileSession> {
|
|
|
|
|
if (this._extensionHostProcessWorker) {
|
|
|
|
|
let port = this._extensionHostProcessWorker.getInspectPort();
|
|
|
|
|
if (port) {
|
|
|
|
|
return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw new Error('Extension host not running or no inspect port available');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
|
|
|
|
|
this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public onExtensionRuntimeError(extensionId: string, err: Error): void {
|
|
|
|
|
if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors[extensionId] = [];
|
|
|
|
|
}
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
public _serviceBrand: any;
|
|
|
|
|
|
|
|
|
|
private _onDidRegisterExtensions: Emitter<void>;
|
|
|
|
|
private readonly _onDidRegisterExtensions: Emitter<void>;
|
|
|
|
|
|
|
|
|
|
private _registry: ExtensionDescriptionRegistry;
|
|
|
|
|
private readonly _installedExtensionsReady: Barrier;
|
|
|
|
|
@@ -126,31 +276,18 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
public readonly onDidChangeExtensionsStatus: Event<string[]> = this._onDidChangeExtensionsStatus.event;
|
|
|
|
|
|
|
|
|
|
// --- Members used per extension host process
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
|
|
|
|
|
*/
|
|
|
|
|
private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
|
|
|
|
|
private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
|
|
|
|
|
private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
|
|
|
|
|
private _extensionHostProcessWorker: ExtensionHostProcessWorker;
|
|
|
|
|
private _extensionHostProcessRPCProtocol: RPCProtocol;
|
|
|
|
|
private _extensionHostProcessCustomers: IDisposable[];
|
|
|
|
|
/**
|
|
|
|
|
* winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object.
|
|
|
|
|
*/
|
|
|
|
|
private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>;
|
|
|
|
|
private _extensionHostProcessManager: ExtensionHostProcessManager;
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
|
|
|
|
@INotificationService private readonly _notificationService: INotificationService,
|
|
|
|
|
@IChoiceService private readonly _choiceService: IChoiceService,
|
|
|
|
|
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
|
|
|
|
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
|
|
|
|
@IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService,
|
|
|
|
|
@IStorageService private readonly _storageService: IStorageService,
|
|
|
|
|
@IWindowService private readonly _windowService: IWindowService,
|
|
|
|
|
@ILifecycleService lifecycleService: ILifecycleService
|
|
|
|
|
@ILifecycleService lifecycleService: ILifecycleService,
|
|
|
|
|
@IExtensionManagementService private extensionManagementService: IExtensionManagementService
|
|
|
|
|
) {
|
|
|
|
|
super();
|
|
|
|
|
this._registry = null;
|
|
|
|
|
@@ -161,15 +298,12 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
|
|
|
|
|
this._onDidRegisterExtensions = new Emitter<void>();
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
|
|
|
|
|
this._extensionHostProcessActivationTimes = Object.create(null);
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors = Object.create(null);
|
|
|
|
|
this._extensionHostProcessWorker = null;
|
|
|
|
|
this._extensionHostProcessRPCProtocol = null;
|
|
|
|
|
this._extensionHostProcessCustomers = [];
|
|
|
|
|
this._extensionHostProcessProxy = null;
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessManager = null;
|
|
|
|
|
this.startDelayed(lifecycleService);
|
|
|
|
|
|
|
|
|
|
if (this._environmentService.disableExtensions) {
|
|
|
|
|
this._notificationService.info(nls.localize('extensionsDisabled', "All extensions are disabled."));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private startDelayed(lifecycleService: ILifecycleService): void {
|
|
|
|
|
@@ -223,51 +357,25 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _stopExtensionHostProcess(): void {
|
|
|
|
|
const previouslyActivatedExtensionIds = Object.keys(this._extensionHostProcessActivationTimes);
|
|
|
|
|
let previouslyActivatedExtensionIds: string[] = [];
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
|
|
|
|
|
this._extensionHostProcessActivationTimes = Object.create(null);
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors = Object.create(null);
|
|
|
|
|
if (this._extensionHostProcessWorker) {
|
|
|
|
|
this._extensionHostProcessWorker.dispose();
|
|
|
|
|
this._extensionHostProcessWorker = null;
|
|
|
|
|
if (this._extensionHostProcessManager) {
|
|
|
|
|
previouslyActivatedExtensionIds = this._extensionHostProcessManager.getActivatedExtensionIds();
|
|
|
|
|
this._extensionHostProcessManager.dispose();
|
|
|
|
|
this._extensionHostProcessManager = null;
|
|
|
|
|
}
|
|
|
|
|
if (this._extensionHostProcessRPCProtocol) {
|
|
|
|
|
this._extensionHostProcessRPCProtocol.dispose();
|
|
|
|
|
this._extensionHostProcessRPCProtocol = null;
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) {
|
|
|
|
|
const customer = this._extensionHostProcessCustomers[i];
|
|
|
|
|
try {
|
|
|
|
|
customer.dispose();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
errors.onUnexpectedError(err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this._extensionHostProcessCustomers = [];
|
|
|
|
|
this._extensionHostProcessProxy = null;
|
|
|
|
|
|
|
|
|
|
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
|
|
|
|
|
if (previouslyActivatedExtensionIds.length > 0) {
|
|
|
|
|
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _startExtensionHostProcess(initialActivationEvents: string[]): void {
|
|
|
|
|
this._stopExtensionHostProcess();
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this);
|
|
|
|
|
this._extensionHostProcessWorker.onCrashed(([code, signal]) => this._onExtensionHostCrashed(code, signal));
|
|
|
|
|
this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then(
|
|
|
|
|
(protocol) => {
|
|
|
|
|
return { value: this._createExtensionHostCustomers(protocol) };
|
|
|
|
|
},
|
|
|
|
|
(err) => {
|
|
|
|
|
console.error('Error received from starting extension host');
|
|
|
|
|
console.error(err);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
this._extensionHostProcessProxy.then(() => {
|
|
|
|
|
initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent));
|
|
|
|
|
});
|
|
|
|
|
const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this);
|
|
|
|
|
this._extensionHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents);
|
|
|
|
|
this._extensionHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _onExtensionHostCrashed(code: number, signal: string): void {
|
|
|
|
|
@@ -279,49 +387,16 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._choiceService.choose(Severity.Error, message, [nls.localize('devTools', "Developer Tools"), nls.localize('restart', "Restart Extension Host")]).then(choice => {
|
|
|
|
|
switch (choice) {
|
|
|
|
|
case 0 /* Open Dev Tools */:
|
|
|
|
|
this._windowService.openDevTools();
|
|
|
|
|
break;
|
|
|
|
|
case 1 /* Restart Extension Host */:
|
|
|
|
|
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
|
|
|
|
|
|
|
|
|
|
if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) {
|
|
|
|
|
protocol = asLoggingProtocol(protocol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol);
|
|
|
|
|
const extHostContext: IExtHostContext = this._extensionHostProcessRPCProtocol;
|
|
|
|
|
|
|
|
|
|
// Named customers
|
|
|
|
|
const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
|
|
|
|
|
for (let i = 0, len = namedCustomers.length; i < len; i++) {
|
|
|
|
|
const [id, ctor] = namedCustomers[i];
|
|
|
|
|
const instance = this._instantiationService.createInstance(ctor, extHostContext);
|
|
|
|
|
this._extensionHostProcessCustomers.push(instance);
|
|
|
|
|
this._extensionHostProcessRPCProtocol.set(id, instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Customers
|
|
|
|
|
const customers = ExtHostCustomersRegistry.getCustomers();
|
|
|
|
|
for (let i = 0, len = customers.length; i < len; i++) {
|
|
|
|
|
const ctor = customers[i];
|
|
|
|
|
const instance = this._instantiationService.createInstance(ctor, extHostContext);
|
|
|
|
|
this._extensionHostProcessCustomers.push(instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that no named customers are missing
|
|
|
|
|
const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => MainContext[key]);
|
|
|
|
|
this._extensionHostProcessRPCProtocol.assertRegistered(expected);
|
|
|
|
|
|
|
|
|
|
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
|
|
|
|
|
this._notificationService.prompt(Severity.Error, message,
|
|
|
|
|
[{
|
|
|
|
|
label: nls.localize('devTools', "Open Developer Tools"),
|
|
|
|
|
run: () => this._windowService.openDevTools()
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: nls.localize('restart', "Restart Extension Host"),
|
|
|
|
|
run: () => this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents))
|
|
|
|
|
}]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- begin IExtensionService
|
|
|
|
|
@@ -349,15 +424,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected _activateByEvent(activationEvent: string): TPromise<void> {
|
|
|
|
|
if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) {
|
|
|
|
|
return NO_OP_VOID_PROMISE;
|
|
|
|
|
private _activateByEvent(activationEvent: string): TPromise<void> {
|
|
|
|
|
if (this._extensionHostProcessManager) {
|
|
|
|
|
return this._extensionHostProcessManager.activateByEvent(activationEvent);
|
|
|
|
|
}
|
|
|
|
|
return this._extensionHostProcessProxy.then((proxy) => {
|
|
|
|
|
return proxy.value.$activateByEvent(activationEvent);
|
|
|
|
|
}).then(() => {
|
|
|
|
|
this._extensionHostProcessFinishedActivateEvents[activationEvent] = true;
|
|
|
|
|
});
|
|
|
|
|
return NO_OP_VOID_PROMISE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public whenInstalledExtensionsRegistered(): TPromise<boolean> {
|
|
|
|
|
@@ -388,6 +459,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } {
|
|
|
|
|
const activationTimes = this._extensionHostProcessManager ? this._extensionHostProcessManager.getActivationTimes() : {};
|
|
|
|
|
const runtimeErrors = this._extensionHostProcessManager ? this._extensionHostProcessManager.getRuntimeErrors() : {};
|
|
|
|
|
|
|
|
|
|
let result: { [id: string]: IExtensionsStatus; } = Object.create(null);
|
|
|
|
|
if (this._registry) {
|
|
|
|
|
const extensions = this._registry.getAllExtensionDescriptions();
|
|
|
|
|
@@ -396,8 +470,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
const id = extension.id;
|
|
|
|
|
result[id] = {
|
|
|
|
|
messages: this._extensionsMessages[id],
|
|
|
|
|
activationTimes: this._extensionHostProcessActivationTimes[id],
|
|
|
|
|
runtimeErrors: this._extensionHostExtensionRuntimeErrors[id],
|
|
|
|
|
activationTimes: activationTimes[id],
|
|
|
|
|
runtimeErrors: runtimeErrors[id],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -405,15 +479,15 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public canProfileExtensionHost(): boolean {
|
|
|
|
|
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
|
|
|
|
|
if (this._extensionHostProcessManager) {
|
|
|
|
|
return this._extensionHostProcessManager.canProfileExtensionHost();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public startExtensionHostProfile(): TPromise<ProfileSession> {
|
|
|
|
|
if (this._extensionHostProcessWorker) {
|
|
|
|
|
let port = this._extensionHostProcessWorker.getInspectPort();
|
|
|
|
|
if (port) {
|
|
|
|
|
return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
|
|
|
|
|
}
|
|
|
|
|
if (this._extensionHostProcessManager) {
|
|
|
|
|
return this._extensionHostProcessManager.startExtensionHostProfile();
|
|
|
|
|
}
|
|
|
|
|
throw new Error('Extension host not running or no inspect port available');
|
|
|
|
|
}
|
|
|
|
|
@@ -424,7 +498,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
|
|
|
|
|
private _scanAndHandleExtensions(): void {
|
|
|
|
|
|
|
|
|
|
this._getRuntimeExtension()
|
|
|
|
|
this._getRuntimeExtensions()
|
|
|
|
|
.then(runtimeExtensons => {
|
|
|
|
|
this._registry = new ExtensionDescriptionRegistry(runtimeExtensons);
|
|
|
|
|
|
|
|
|
|
@@ -449,14 +523,13 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _getRuntimeExtension(): TPromise<IExtensionDescription[]> {
|
|
|
|
|
private _getRuntimeExtensions(): TPromise<IExtensionDescription[]> {
|
|
|
|
|
const log = new Logger((severity, source, message) => {
|
|
|
|
|
this._logOrShowMessage(severity, this._isDev ? messageWithSource2(source, message) : message);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return ExtensionService._scanInstalledExtensions(this._windowService, this._choiceService, this._environmentService, log)
|
|
|
|
|
return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, log)
|
|
|
|
|
.then(({ system, user, development }) => {
|
|
|
|
|
this._extensionEnablementService.migrateToIdentifiers(user); // TODO: @sandy Remove it after couple of milestones
|
|
|
|
|
return this._extensionEnablementService.getDisabledExtensions()
|
|
|
|
|
.then(disabledExtensions => {
|
|
|
|
|
let result: { [extensionId: string]: IExtensionDescription; } = {};
|
|
|
|
|
@@ -501,7 +574,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (extensionsToDisable.length) {
|
|
|
|
|
return TPromise.join(extensionsToDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled)))
|
|
|
|
|
return this.extensionManagementService.getInstalled(LocalExtensionType.User)
|
|
|
|
|
.then(installed => {
|
|
|
|
|
const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e)));
|
|
|
|
|
return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled)));
|
|
|
|
|
})
|
|
|
|
|
.then(() => {
|
|
|
|
|
this._storageService.store(BetterMergeDisabledNowKey, true);
|
|
|
|
|
return runtimeExtensions;
|
|
|
|
|
@@ -510,7 +587,36 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
return runtimeExtensions;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}).then(extensions => this._updateEnableProposedApi(extensions));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _updateEnableProposedApi(extensions: IExtensionDescription[]): IExtensionDescription[] {
|
|
|
|
|
const enableProposedApiForAll = !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0);
|
|
|
|
|
const enableProposedApiFor = this._environmentService.args['enable-proposed-api'] || [];
|
|
|
|
|
for (const extension of extensions) {
|
|
|
|
|
if (!isFalsyOrEmpty(product.extensionAllowedProposedApi)
|
|
|
|
|
&& product.extensionAllowedProposedApi.indexOf(extension.id) >= 0
|
|
|
|
|
) {
|
|
|
|
|
// fast lane -> proposed api is available to all extensions
|
|
|
|
|
// that are listed in product.json-files
|
|
|
|
|
extension.enableProposedApi = true;
|
|
|
|
|
|
|
|
|
|
} else if (extension.enableProposedApi && !extension.isBuiltin) {
|
|
|
|
|
if (
|
|
|
|
|
!enableProposedApiForAll &&
|
|
|
|
|
enableProposedApiFor.indexOf(extension.id) < 0
|
|
|
|
|
) {
|
|
|
|
|
extension.enableProposedApi = false;
|
|
|
|
|
console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// proposed api is available when developing or when an extension was explicitly
|
|
|
|
|
// spelled out via a command line argument
|
|
|
|
|
console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return extensions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _handleExtensionPointMessage(msg: IMessage) {
|
|
|
|
|
@@ -531,7 +637,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
const { type, extensionId, extensionPointId, message } = msg;
|
|
|
|
|
/* __GDPR__
|
|
|
|
|
"extensionsMessage" : {
|
|
|
|
|
"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
|
|
|
|
"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
|
|
|
|
"extensionId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
|
|
|
|
"extensionPointId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
|
|
|
|
"message": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
|
|
|
|
@@ -543,7 +649,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async _validateExtensionsCache(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
|
|
|
|
|
private static async _validateExtensionsCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
|
|
|
|
|
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
|
|
|
|
|
const cacheFile = path.join(cacheFolder, cacheKey);
|
|
|
|
|
|
|
|
|
|
@@ -568,11 +674,14 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
console.error(err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
choiceService.choose(Severity.Error, nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."), [nls.localize('reloadWindow', "Reload Window")]).then(choice => {
|
|
|
|
|
if (choice === 0) {
|
|
|
|
|
windowService.reloadWindow();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
notificationService.prompt(
|
|
|
|
|
Severity.Error,
|
|
|
|
|
nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),
|
|
|
|
|
[{
|
|
|
|
|
label: nls.localize('reloadWindow', "Reload Window"),
|
|
|
|
|
run: () => windowService.reloadWindow()
|
|
|
|
|
}]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async _readExtensionCache(environmentService: IEnvironmentService, cacheKey: string): TPromise<IExtensionCacheData> {
|
|
|
|
|
@@ -606,7 +715,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async _scanExtensionsWithCache(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
|
|
|
|
private static async _scanExtensionsWithCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
|
|
|
|
if (input.devMode) {
|
|
|
|
|
// Do not cache when running out of sources...
|
|
|
|
|
return ExtensionScanner.scanExtensions(input, log);
|
|
|
|
|
@@ -624,7 +733,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
// Validate the cache asynchronously after 5s
|
|
|
|
|
setTimeout(async () => {
|
|
|
|
|
try {
|
|
|
|
|
await this._validateExtensionsCache(windowService, choiceService, environmentService, cacheKey, input);
|
|
|
|
|
await this._validateExtensionsCache(windowService, notificationService, environmentService, cacheKey, input);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
errors.onUnexpectedError(err);
|
|
|
|
|
}
|
|
|
|
|
@@ -646,7 +755,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static _scanInstalledExtensions(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
|
|
|
|
|
private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
|
|
|
|
|
|
|
|
|
|
const translationConfig: TPromise<Translations> = platform.translationsConfigFile
|
|
|
|
|
? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => {
|
|
|
|
|
@@ -668,7 +777,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
|
|
|
|
|
const builtinExtensions = this._scanExtensionsWithCache(
|
|
|
|
|
windowService,
|
|
|
|
|
choiceService,
|
|
|
|
|
notificationService,
|
|
|
|
|
environmentService,
|
|
|
|
|
BUILTIN_MANIFEST_CACHE_FILE,
|
|
|
|
|
new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, translations),
|
|
|
|
|
@@ -722,7 +831,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
? TPromise.as([])
|
|
|
|
|
: this._scanExtensionsWithCache(
|
|
|
|
|
windowService,
|
|
|
|
|
choiceService,
|
|
|
|
|
notificationService,
|
|
|
|
|
environmentService,
|
|
|
|
|
USER_MANIFEST_CACHE_FILE,
|
|
|
|
|
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, translations),
|
|
|
|
|
@@ -798,15 +907,12 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public _onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
|
|
|
|
|
this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
|
|
|
|
|
this._extensionHostProcessManager.onExtensionActivated(extensionId, startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
|
|
|
|
|
this._onDidChangeExtensionsStatus.fire([extensionId]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public _onExtensionRuntimeError(extensionId: string, err: Error): void {
|
|
|
|
|
if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors[extensionId] = [];
|
|
|
|
|
}
|
|
|
|
|
this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
|
|
|
|
|
this._extensionHostProcessManager.onExtensionRuntimeError(extensionId, err);
|
|
|
|
|
this._onDidChangeExtensionsStatus.fire([extensionId]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|