Path: blob/main/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Barrier } from '../../../../base/common/async.js';6import { toErrorMessage } from '../../../../base/common/errorMessage.js';7import { Emitter } from '../../../../base/common/event.js';8import { IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';9import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';10import { Schemas } from '../../../../base/common/network.js';11import * as perf from '../../../../base/common/performance.js';12import { isCI } from '../../../../base/common/platform.js';13import { isEqualOrParent } from '../../../../base/common/resources.js';14import { StopWatch } from '../../../../base/common/stopwatch.js';15import { isDefined } from '../../../../base/common/types.js';16import { URI } from '../../../../base/common/uri.js';17import * as nls from '../../../../nls.js';18import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';19import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';20import { InstallOperation } from '../../../../platform/extensionManagement/common/extensionManagement.js';21import { ImplicitActivationEvents } from '../../../../platform/extensionManagement/common/implicitActivationEvents.js';22import { ExtensionIdentifier, ExtensionIdentifierMap, IExtension, IExtensionContributions, IExtensionDescription, IExtensionManifest } from '../../../../platform/extensions/common/extensions.js';23import { IFileService } from '../../../../platform/files/common/files.js';24import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';25import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';26import { handleVetos } from '../../../../platform/lifecycle/common/lifecycle.js';27import { ILogService } from '../../../../platform/log/common/log.js';28import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';29import { IProductService } from '../../../../platform/product/common/productService.js';30import { Registry } from '../../../../platform/registry/common/platform.js';31import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode, ResolverResult, getRemoteAuthorityPrefix } from '../../../../platform/remote/common/remoteAuthorityResolver.js';32import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js';33import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';34import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';35import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';36import { IExtensionFeaturesRegistry, Extensions as ExtensionFeaturesExtensions, IExtensionFeatureMarkdownRenderer, IRenderedData, } from '../../extensionManagement/common/extensionFeatures.js';37import { IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../extensionManagement/common/extensionManagement.js';38import { ExtensionDescriptionRegistryLock, ExtensionDescriptionRegistrySnapshot, IActivationEventsReader, LockableExtensionDescriptionRegistry } from './extensionDescriptionRegistry.js';39import { parseExtensionDevOptions } from './extensionDevOptions.js';40import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker } from './extensionHostKind.js';41import { ExtensionHostManager } from './extensionHostManager.js';42import { IExtensionHostManager } from './extensionHostManagers.js';43import { IResolveAuthorityErrorResult } from './extensionHostProxy.js';44import { IExtensionManifestPropertiesService } from './extensionManifestPropertiesService.js';45import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation, RemoteRunningLocation } from './extensionRunningLocation.js';46import { ExtensionRunningLocationTracker, filterExtensionIdentifiers } from './extensionRunningLocationTracker.js';47import { ActivationKind, ActivationTimes, ExtensionActivationReason, ExtensionHostStartup, ExtensionPointContribution, IExtensionHost, IExtensionInspectInfo, IExtensionService, IExtensionsStatus, IInternalExtensionService, IMessage, IResponsiveStateChangeEvent, IWillActivateEvent, WillStopExtensionHostsEvent, toExtension, toExtensionDescription } from './extensions.js';48import { ExtensionsProposedApi } from './extensionsProposedApi.js';49import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from './extensionsRegistry.js';50import { LazyCreateExtensionHostManager } from './lazyCreateExtensionHostManager.js';51import { ResponsiveState } from './rpcProtocol.js';52import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkActivateWorkspaceContainsExtension, checkGlobFileExists } from './workspaceContains.js';53import { ILifecycleService, WillShutdownJoinerOrder } from '../../lifecycle/common/lifecycle.js';54import { IExtensionHostExitInfo, IRemoteAgentService } from '../../remote/common/remoteAgentService.js';5556const hasOwnProperty = Object.hasOwnProperty;57const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);5859export abstract class AbstractExtensionService extends Disposable implements IExtensionService {6061public _serviceBrand: undefined;6263private readonly _hasLocalProcess: boolean;64private readonly _allowRemoteExtensionsInLocalWebWorker: boolean;6566private readonly _onDidRegisterExtensions = this._register(new Emitter<void>());67public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event;6869private readonly _onDidChangeExtensionsStatus = this._register(new Emitter<ExtensionIdentifier[]>());70public readonly onDidChangeExtensionsStatus = this._onDidChangeExtensionsStatus.event;7172private readonly _onDidChangeExtensions = this._register(new Emitter<{ readonly added: ReadonlyArray<IExtensionDescription>; readonly removed: ReadonlyArray<IExtensionDescription> }>({ leakWarningThreshold: 400 }));73public readonly onDidChangeExtensions = this._onDidChangeExtensions.event;7475private readonly _onWillActivateByEvent = this._register(new Emitter<IWillActivateEvent>());76public readonly onWillActivateByEvent = this._onWillActivateByEvent.event;7778private readonly _onDidChangeResponsiveChange = this._register(new Emitter<IResponsiveStateChangeEvent>());79public readonly onDidChangeResponsiveChange = this._onDidChangeResponsiveChange.event;8081private readonly _onWillStop = this._register(new Emitter<WillStopExtensionHostsEvent>());82public readonly onWillStop = this._onWillStop.event;8384private readonly _activationEventReader = new ImplicitActivationAwareReader();85private readonly _registry = new LockableExtensionDescriptionRegistry(this._activationEventReader);86private readonly _installedExtensionsReady = new Barrier();87private readonly _extensionStatus = new ExtensionIdentifierMap<ExtensionStatus>();88private readonly _allRequestedActivateEvents = new Set<string>();89private readonly _runningLocations: ExtensionRunningLocationTracker;90private readonly _remoteCrashTracker = new ExtensionHostCrashTracker();9192private _deltaExtensionsQueue: DeltaExtensionsQueueItem[] = [];93private _inHandleDeltaExtensions = false;9495private readonly _extensionHostManagers = this._register(new ExtensionHostCollection());9697private _resolveAuthorityAttempt: number = 0;9899constructor(100options: { hasLocalProcess: boolean; allowRemoteExtensionsInLocalWebWorker: boolean },101private readonly _extensionsProposedApi: ExtensionsProposedApi,102private readonly _extensionHostFactory: IExtensionHostFactory,103private readonly _extensionHostKindPicker: IExtensionHostKindPicker,104@IInstantiationService protected readonly _instantiationService: IInstantiationService,105@INotificationService protected readonly _notificationService: INotificationService,106@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,107@ITelemetryService protected readonly _telemetryService: ITelemetryService,108@IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,109@IFileService protected readonly _fileService: IFileService,110@IProductService protected readonly _productService: IProductService,111@IWorkbenchExtensionManagementService protected readonly _extensionManagementService: IWorkbenchExtensionManagementService,112@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,113@IConfigurationService protected readonly _configurationService: IConfigurationService,114@IExtensionManifestPropertiesService private readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService,115@ILogService protected readonly _logService: ILogService,116@IRemoteAgentService protected readonly _remoteAgentService: IRemoteAgentService,117@IRemoteExtensionsScannerService protected readonly _remoteExtensionsScannerService: IRemoteExtensionsScannerService,118@ILifecycleService private readonly _lifecycleService: ILifecycleService,119@IRemoteAuthorityResolverService protected readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,120@IDialogService private readonly _dialogService: IDialogService,121) {122super();123124this._hasLocalProcess = options.hasLocalProcess;125this._allowRemoteExtensionsInLocalWebWorker = options.allowRemoteExtensionsInLocalWebWorker;126127// help the file service to activate providers by activating extensions by file system event128this._register(this._fileService.onWillActivateFileSystemProvider(e => {129if (e.scheme !== Schemas.vscodeRemote) {130e.join(this.activateByEvent(`onFileSystem:${e.scheme}`));131}132}));133134this._runningLocations = new ExtensionRunningLocationTracker(135this._registry,136this._extensionHostKindPicker,137this._environmentService,138this._configurationService,139this._logService,140this._extensionManifestPropertiesService141);142143this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {144const toAdd: IExtension[] = [];145const toRemove: IExtension[] = [];146for (const extension of extensions) {147if (this._safeInvokeIsEnabled(extension)) {148// an extension has been enabled149toAdd.push(extension);150} else {151// an extension has been disabled152toRemove.push(extension);153}154}155if (isCI) {156this._logService.info(`AbstractExtensionService.onEnablementChanged fired for ${extensions.map(e => e.identifier.id).join(', ')}`);157}158this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove));159}));160161this._register(this._extensionManagementService.onDidChangeProfile(({ added, removed }) => {162if (added.length || removed.length) {163if (isCI) {164this._logService.info(`AbstractExtensionService.onDidChangeProfile fired`);165}166this._handleDeltaExtensions(new DeltaExtensionsQueueItem(added, removed));167}168}));169170this._register(this._extensionManagementService.onDidEnableExtensions(extensions => {171if (extensions.length) {172if (isCI) {173this._logService.info(`AbstractExtensionService.onDidEnableExtensions fired`);174}175this._handleDeltaExtensions(new DeltaExtensionsQueueItem(extensions, []));176}177}));178179this._register(this._extensionManagementService.onDidInstallExtensions((result) => {180const extensions: IExtension[] = [];181for (const { local, operation } of result) {182if (local && local.isValid && operation !== InstallOperation.Migrate && this._safeInvokeIsEnabled(local)) {183extensions.push(local);184}185}186if (extensions.length) {187if (isCI) {188this._logService.info(`AbstractExtensionService.onDidInstallExtensions fired for ${extensions.map(e => e.identifier.id).join(', ')}`);189}190this._handleDeltaExtensions(new DeltaExtensionsQueueItem(extensions, []));191}192}));193194this._register(this._extensionManagementService.onDidUninstallExtension((event) => {195if (!event.error) {196// an extension has been uninstalled197if (isCI) {198this._logService.info(`AbstractExtensionService.onDidUninstallExtension fired for ${event.identifier.id}`);199}200this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id]));201}202}));203204this._register(this._lifecycleService.onWillShutdown(event => {205if (this._remoteAgentService.getConnection()) {206event.join(async () => {207// We need to disconnect the management connection before killing the local extension host.208// Otherwise, the local extension host might terminate the underlying tunnel before the209// management connection has a chance to send its disconnection message.210try {211await this._remoteAgentService.endConnection();212await this._doStopExtensionHosts();213this._remoteAgentService.getConnection()?.dispose();214} catch {215this._logService.warn('Error while disconnecting remote agent');216}217}, {218id: 'join.disconnectRemote',219label: nls.localize('disconnectRemote', "Disconnect Remote Agent"),220order: WillShutdownJoinerOrder.Last // after others have joined that might depend on a remote connection221});222} else {223event.join(this._doStopExtensionHosts(), {224id: 'join.stopExtensionHosts',225label: nls.localize('stopExtensionHosts', "Stopping Extension Hosts"),226});227}228}));229}230231protected _getExtensionHostManagers(kind: ExtensionHostKind): IExtensionHostManager[] {232return this._extensionHostManagers.getByKind(kind);233}234235//#region deltaExtensions236237private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise<void> {238this._deltaExtensionsQueue.push(item);239if (this._inHandleDeltaExtensions) {240// Let the current item finish, the new one will be picked up241return;242}243244let lock: ExtensionDescriptionRegistryLock | null = null;245try {246this._inHandleDeltaExtensions = true;247248// wait for _initialize to finish before hanlding any delta extension events249await this._installedExtensionsReady.wait();250251lock = await this._registry.acquireLock('handleDeltaExtensions');252while (this._deltaExtensionsQueue.length > 0) {253const item = this._deltaExtensionsQueue.shift()!;254await this._deltaExtensions(lock, item.toAdd, item.toRemove);255}256} finally {257this._inHandleDeltaExtensions = false;258lock?.dispose();259}260}261262private async _deltaExtensions(lock: ExtensionDescriptionRegistryLock, _toAdd: IExtension[], _toRemove: string[] | IExtension[]): Promise<void> {263if (isCI) {264this._logService.info(`AbstractExtensionService._deltaExtensions: toAdd: [${_toAdd.map(e => e.identifier.id).join(',')}] toRemove: [${_toRemove.map(e => typeof e === 'string' ? e : e.identifier.id).join(',')}]`);265}266let toRemove: IExtensionDescription[] = [];267for (let i = 0, len = _toRemove.length; i < len; i++) {268const extensionOrId = _toRemove[i];269const extensionId = (typeof extensionOrId === 'string' ? extensionOrId : extensionOrId.identifier.id);270const extension = (typeof extensionOrId === 'string' ? null : extensionOrId);271const extensionDescription = this._registry.getExtensionDescription(extensionId);272if (!extensionDescription) {273// ignore disabling/uninstalling an extension which is not running274continue;275}276277if (extension && extensionDescription.extensionLocation.scheme !== extension.location.scheme) {278// this event is for a different extension than mine (maybe for the local extension, while I have the remote extension)279continue;280}281282if (!this.canRemoveExtension(extensionDescription)) {283// uses non-dynamic extension point or is activated284continue;285}286287toRemove.push(extensionDescription);288}289290const toAdd: IExtensionDescription[] = [];291for (let i = 0, len = _toAdd.length; i < len; i++) {292const extension = _toAdd[i];293294const extensionDescription = toExtensionDescription(extension, false);295if (!extensionDescription) {296// could not scan extension...297continue;298}299300if (!this._canAddExtension(extensionDescription, toRemove)) {301continue;302}303304toAdd.push(extensionDescription);305}306307if (toAdd.length === 0 && toRemove.length === 0) {308return;309}310311// Update the local registry312const result = this._registry.deltaExtensions(lock, toAdd, toRemove.map(e => e.identifier));313this._onDidChangeExtensions.fire({ added: toAdd, removed: toRemove });314315toRemove = toRemove.concat(result.removedDueToLooping);316if (result.removedDueToLooping.length > 0) {317this._notificationService.notify({318severity: Severity.Error,319message: nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))320});321}322323// enable or disable proposed API per extension324this._extensionsProposedApi.updateEnabledApiProposals(toAdd);325326// Update extension points327this._doHandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove), false);328329// Update the extension host330await this._updateExtensionsOnExtHosts(result.versionId, toAdd, toRemove.map(e => e.identifier));331332for (let i = 0; i < toAdd.length; i++) {333this._activateAddedExtensionIfNeeded(toAdd[i]);334}335}336337private async _updateExtensionsOnExtHosts(versionId: number, toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {338const removedRunningLocation = this._runningLocations.deltaExtensions(toAdd, toRemove);339const promises = this._extensionHostManagers.map(340extHostManager => this._updateExtensionsOnExtHost(extHostManager, versionId, toAdd, toRemove, removedRunningLocation)341);342await Promise.all(promises);343}344345private async _updateExtensionsOnExtHost(extensionHostManager: IExtensionHostManager, versionId: number, toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[], removedRunningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>): Promise<void> {346const myToAdd = this._runningLocations.filterByExtensionHostManager(toAdd, extensionHostManager);347const myToRemove = filterExtensionIdentifiers(toRemove, removedRunningLocation, extRunningLocation => extensionHostManager.representsRunningLocation(extRunningLocation));348const addActivationEvents = ImplicitActivationEvents.createActivationEventsMap(toAdd);349if (isCI) {350const printExtIds = (extensions: IExtensionDescription[]) => extensions.map(e => e.identifier.value).join(',');351const printIds = (extensions: ExtensionIdentifier[]) => extensions.map(e => e.value).join(',');352this._logService.info(`AbstractExtensionService: Calling deltaExtensions: toRemove: [${printIds(toRemove)}], toAdd: [${printExtIds(toAdd)}], myToRemove: [${printIds(myToRemove)}], myToAdd: [${printExtIds(myToAdd)}],`);353}354await extensionHostManager.deltaExtensions({ versionId, toRemove, toAdd, addActivationEvents, myToRemove, myToAdd: myToAdd.map(extension => extension.identifier) });355}356357public canAddExtension(extension: IExtensionDescription): boolean {358return this._canAddExtension(extension, []);359}360361private _canAddExtension(extension: IExtensionDescription, extensionsBeingRemoved: IExtensionDescription[]): boolean {362// (Also check for renamed extensions)363const existing = this._registry.getExtensionDescriptionByIdOrUUID(extension.identifier, extension.id);364if (existing) {365// This extension is already known (most likely at a different version)366// so it cannot be added again unless it is removed first367const isBeingRemoved = extensionsBeingRemoved.some((extensionDescription) => ExtensionIdentifier.equals(extension.identifier, extensionDescription.identifier));368if (!isBeingRemoved) {369return false;370}371}372373const extensionKinds = this._runningLocations.readExtensionKinds(extension);374const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;375const extensionHostKind = this._extensionHostKindPicker.pickExtensionHostKind(extension.identifier, extensionKinds, !isRemote, isRemote, ExtensionRunningPreference.None);376if (extensionHostKind === null) {377return false;378}379380return true;381}382383public canRemoveExtension(extension: IExtensionDescription): boolean {384const extensionDescription = this._registry.getExtensionDescription(extension.identifier);385if (!extensionDescription) {386// Can't remove an extension that is unknown!387return false;388}389390if (this._extensionStatus.get(extensionDescription.identifier)?.activationStarted) {391// Extension is running, cannot remove it safely392return false;393}394395return true;396}397398private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise<void> {399let shouldActivate = false;400let shouldActivateReason: string | null = null;401let hasWorkspaceContains = false;402const activationEvents = this._activationEventReader.readActivationEvents(extensionDescription);403for (const activationEvent of activationEvents) {404if (this._allRequestedActivateEvents.has(activationEvent)) {405// This activation event was fired before the extension was added406shouldActivate = true;407shouldActivateReason = activationEvent;408break;409}410411if (activationEvent === '*') {412shouldActivate = true;413shouldActivateReason = activationEvent;414break;415}416417if (/^workspaceContains/.test(activationEvent)) {418hasWorkspaceContains = true;419}420421if (activationEvent === 'onStartupFinished') {422shouldActivate = true;423shouldActivateReason = activationEvent;424break;425}426}427428if (shouldActivate) {429await Promise.all(430this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! }))431).then(() => { });432} else if (hasWorkspaceContains) {433const workspace = await this._contextService.getCompleteWorkspace();434const forceUsingSearch = !!this._environmentService.remoteAuthority;435const host: IWorkspaceContainsActivationHost = {436logService: this._logService,437folders: workspace.folders.map(folder => folder.uri),438forceUsingSearch: forceUsingSearch,439exists: (uri) => this._fileService.exists(uri),440checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token))441};442443const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription);444if (!result) {445return;446}447448await Promise.all(449this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent }))450).then(() => { });451}452}453454//#endregion455456protected async _initialize(): Promise<void> {457perf.mark('code/willLoadExtensions');458this._startExtensionHostsIfNecessary(true, []);459460const lock = await this._registry.acquireLock('_initialize');461try {462await this._resolveAndProcessExtensions(lock);463// Start extension hosts which are not automatically started464this._startOnDemandExtensionHosts();465} finally {466lock.dispose();467}468469this._releaseBarrier();470perf.mark('code/didLoadExtensions');471await this._handleExtensionTests();472}473474private async _resolveAndProcessExtensions(lock: ExtensionDescriptionRegistryLock,): Promise<void> {475let resolverExtensions: IExtensionDescription[] = [];476let localExtensions: IExtensionDescription[] = [];477let remoteExtensions: IExtensionDescription[] = [];478479for await (const extensions of this._resolveExtensions()) {480if (extensions instanceof ResolverExtensions) {481resolverExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, extensions.extensions, false);482this._registry.deltaExtensions(lock, resolverExtensions, []);483this._doHandleExtensionPoints(resolverExtensions, true);484}485if (extensions instanceof LocalExtensions) {486localExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, extensions.extensions, false);487}488if (extensions instanceof RemoteExtensions) {489remoteExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, extensions.extensions, false);490}491}492493// `initializeRunningLocation` will look at the complete picture (e.g. an extension installed on both sides),494// takes care of duplicates and picks a running location for each extension495this._runningLocations.initializeRunningLocation(localExtensions, remoteExtensions);496497this._startExtensionHostsIfNecessary(true, []);498499// Some remote extensions could run locally in the web worker, so store them500const remoteExtensionsThatNeedToRunLocally = (this._allowRemoteExtensionsInLocalWebWorker ? this._runningLocations.filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.LocalWebWorker) : []);501const localProcessExtensions = (this._hasLocalProcess ? this._runningLocations.filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalProcess) : []);502const localWebWorkerExtensions = this._runningLocations.filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);503remoteExtensions = this._runningLocations.filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);504505// Add locally the remote extensions that need to run locally in the web worker506for (const ext of remoteExtensionsThatNeedToRunLocally) {507if (!includes(localWebWorkerExtensions, ext.identifier)) {508localWebWorkerExtensions.push(ext);509}510}511512const allExtensions = remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions);513let toAdd = allExtensions;514515if (resolverExtensions.length) {516// Add extensions that are not registered as resolvers but are in the final resolved set517toAdd = allExtensions.filter(extension => !resolverExtensions.some(e => ExtensionIdentifier.equals(e.identifier, extension.identifier) && e.extensionLocation.toString() === extension.extensionLocation.toString()));518// Remove extensions that are registered as resolvers but are not in the final resolved set519if (allExtensions.length < toAdd.length + resolverExtensions.length) {520const toRemove = resolverExtensions.filter(registered => !allExtensions.some(e => ExtensionIdentifier.equals(e.identifier, registered.identifier) && e.extensionLocation.toString() === registered.extensionLocation.toString()));521if (toRemove.length) {522this._registry.deltaExtensions(lock, [], toRemove.map(e => e.identifier));523this._doHandleExtensionPoints(toRemove, true);524}525}526}527528const result = this._registry.deltaExtensions(lock, toAdd, []);529if (result.removedDueToLooping.length > 0) {530this._notificationService.notify({531severity: Severity.Error,532message: nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))533});534}535536this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions(), false);537}538539private async _handleExtensionTests(): Promise<void> {540if (!this._environmentService.isExtensionDevelopment || !this._environmentService.extensionTestsLocationURI) {541return;542}543544const extensionHostManager = this.findTestExtensionHost(this._environmentService.extensionTestsLocationURI);545if (!extensionHostManager) {546const msg = nls.localize('extensionTestError', "No extension host found that can launch the test runner at {0}.", this._environmentService.extensionTestsLocationURI.toString());547console.error(msg);548this._notificationService.error(msg);549return;550}551552553let exitCode: number;554try {555exitCode = await extensionHostManager.extensionTestsExecute();556if (isCI) {557this._logService.info(`Extension host test runner exit code: ${exitCode}`);558}559} catch (err) {560if (isCI) {561this._logService.error(`Extension host test runner error`, err);562}563console.error(err);564exitCode = 1 /* ERROR */;565}566567this._onExtensionHostExit(exitCode);568}569570private findTestExtensionHost(testLocation: URI): IExtensionHostManager | null {571let runningLocation: ExtensionRunningLocation | null = null;572573for (const extension of this._registry.getAllExtensionDescriptions()) {574if (isEqualOrParent(testLocation, extension.extensionLocation)) {575runningLocation = this._runningLocations.getRunningLocation(extension.identifier);576break;577}578}579if (runningLocation === null) {580// not sure if we should support that, but it was possible to have an test outside an extension581582if (testLocation.scheme === Schemas.vscodeRemote) {583runningLocation = new RemoteRunningLocation();584} else {585// When a debugger attaches to the extension host, it will surface all console.log messages from the extension host,586// but not necessarily from the window. So it would be best if any errors get printed to the console of the extension host.587// That is why here we use the local process extension host even for non-file URIs588runningLocation = new LocalProcessRunningLocation(0);589}590}591if (runningLocation !== null) {592return this._extensionHostManagers.getByRunningLocation(runningLocation);593}594return null;595}596597private _releaseBarrier(): void {598this._installedExtensionsReady.open();599this._onDidRegisterExtensions.fire(undefined);600this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier));601}602603//#region remote authority resolving604605protected async _resolveAuthorityInitial(remoteAuthority: string): Promise<ResolverResult> {606const MAX_ATTEMPTS = 5;607608for (let attempt = 1; ; attempt++) {609try {610return this._resolveAuthorityWithLogging(remoteAuthority);611} catch (err) {612if (RemoteAuthorityResolverError.isNoResolverFound(err)) {613// There is no point in retrying if there is no resolver found614throw err;615}616617if (RemoteAuthorityResolverError.isNotAvailable(err)) {618// The resolver is not available and asked us to not retry619throw err;620}621622if (attempt >= MAX_ATTEMPTS) {623// Too many failed attempts, give up624throw err;625}626}627}628}629630protected async _resolveAuthorityAgain(): Promise<void> {631const remoteAuthority = this._environmentService.remoteAuthority;632if (!remoteAuthority) {633return;634}635636this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);637try {638const result = await this._resolveAuthorityWithLogging(remoteAuthority);639this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options);640} catch (err) {641this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);642}643}644645private async _resolveAuthorityWithLogging(remoteAuthority: string): Promise<ResolverResult> {646const authorityPrefix = getRemoteAuthorityPrefix(remoteAuthority);647const sw = StopWatch.create(false);648this._logService.info(`Invoking resolveAuthority(${authorityPrefix})...`);649try {650perf.mark(`code/willResolveAuthority/${authorityPrefix}`);651const result = await this._resolveAuthority(remoteAuthority);652perf.mark(`code/didResolveAuthorityOK/${authorityPrefix}`);653this._logService.info(`resolveAuthority(${authorityPrefix}) returned '${result.authority.connectTo}' after ${sw.elapsed()} ms`);654return result;655} catch (err) {656perf.mark(`code/didResolveAuthorityError/${authorityPrefix}`);657this._logService.error(`resolveAuthority(${authorityPrefix}) returned an error after ${sw.elapsed()} ms`, err);658throw err;659}660}661662protected async _resolveAuthorityOnExtensionHosts(kind: ExtensionHostKind, remoteAuthority: string): Promise<ResolverResult> {663664const extensionHosts = this._getExtensionHostManagers(kind);665if (extensionHosts.length === 0) {666// no local process extension hosts667throw new Error(`Cannot resolve authority`);668}669670this._resolveAuthorityAttempt++;671const results = await Promise.all(extensionHosts.map(extHost => extHost.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt)));672673let bestErrorResult: IResolveAuthorityErrorResult | null = null;674for (const result of results) {675if (result.type === 'ok') {676return result.value;677}678if (!bestErrorResult) {679bestErrorResult = result;680continue;681}682const bestErrorIsUnknown = (bestErrorResult.error.code === RemoteAuthorityResolverErrorCode.Unknown);683const errorIsUnknown = (result.error.code === RemoteAuthorityResolverErrorCode.Unknown);684if (bestErrorIsUnknown && !errorIsUnknown) {685bestErrorResult = result;686}687}688689// we can only reach this if there is an error690throw new RemoteAuthorityResolverError(bestErrorResult!.error.message, bestErrorResult!.error.code, bestErrorResult!.error.detail);691}692693//#endregion694695//#region Stopping / Starting / Restarting696697public stopExtensionHosts(reason: string, auto?: boolean): Promise<boolean> {698return this._doStopExtensionHostsWithVeto(reason, auto);699}700701protected async _doStopExtensionHosts(): Promise<void> {702const previouslyActivatedExtensionIds: ExtensionIdentifier[] = [];703for (const extensionStatus of this._extensionStatus.values()) {704if (extensionStatus.activationStarted) {705previouslyActivatedExtensionIds.push(extensionStatus.id);706}707}708709await this._extensionHostManagers.stopAllInReverse();710for (const extensionStatus of this._extensionStatus.values()) {711extensionStatus.clearRuntimeStatus();712}713714if (previouslyActivatedExtensionIds.length > 0) {715this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);716}717}718719private async _doStopExtensionHostsWithVeto(reason: string, auto: boolean = false): Promise<boolean> {720if (auto && this._environmentService.isExtensionDevelopment) {721return false;722}723724const vetos: (boolean | Promise<boolean>)[] = [];725const vetoReasons = new Set<string>();726727this._onWillStop.fire({728reason,729auto,730veto(value, reason) {731vetos.push(value);732733if (typeof value === 'boolean') {734if (value === true) {735vetoReasons.add(reason);736}737} else {738value.then(value => {739if (value) {740vetoReasons.add(reason);741}742}).catch(error => {743vetoReasons.add(nls.localize('extensionStopVetoError', "{0} (Error: {1})", reason, toErrorMessage(error)));744});745}746}747});748749const veto = await handleVetos(vetos, error => this._logService.error(error));750if (!veto) {751await this._doStopExtensionHosts();752} else {753if (!auto) {754const vetoReasonsArray = Array.from(vetoReasons);755756this._logService.warn(`Extension host was not stopped because of veto (stop reason: ${reason}, veto reason: ${vetoReasonsArray.join(', ')})`);757758const { confirmed } = await this._dialogService.confirm({759type: Severity.Warning,760message: nls.localize('extensionStopVetoMessage', "Please confirm restart of extensions."),761detail: vetoReasonsArray.length === 1 ?762vetoReasonsArray[0] :763vetoReasonsArray.join('\n -'),764primaryButton: nls.localize('proceedAnyways', "Restart Anyway")765});766767if (confirmed) {768return true;769}770}771772}773774return !veto;775}776777private _startExtensionHostsIfNecessary(isInitialStart: boolean, initialActivationEvents: string[]): void {778const locations: ExtensionRunningLocation[] = [];779for (let affinity = 0; affinity <= this._runningLocations.maxLocalProcessAffinity; affinity++) {780locations.push(new LocalProcessRunningLocation(affinity));781}782for (let affinity = 0; affinity <= this._runningLocations.maxLocalWebWorkerAffinity; affinity++) {783locations.push(new LocalWebWorkerRunningLocation(affinity));784}785locations.push(new RemoteRunningLocation());786for (const location of locations) {787if (this._extensionHostManagers.getByRunningLocation(location)) {788// already running789continue;790}791const res = this._createExtensionHostManager(location, isInitialStart, initialActivationEvents);792if (res) {793const [extHostManager, disposableStore] = res;794this._extensionHostManagers.add(extHostManager, disposableStore);795}796}797}798799private _createExtensionHostManager(runningLocation: ExtensionRunningLocation, isInitialStart: boolean, initialActivationEvents: string[]): null | [IExtensionHostManager, DisposableStore] {800const extensionHost = this._extensionHostFactory.createExtensionHost(this._runningLocations, runningLocation, isInitialStart);801if (!extensionHost) {802return null;803}804805const processManager: IExtensionHostManager = this._doCreateExtensionHostManager(extensionHost, initialActivationEvents);806const disposableStore = new DisposableStore();807disposableStore.add(processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)));808disposableStore.add(processManager.onDidChangeResponsiveState((responsiveState) => {809this._logService.info(`Extension host (${processManager.friendyName}) is ${responsiveState === ResponsiveState.Responsive ? 'responsive' : 'unresponsive'}.`);810this._onDidChangeResponsiveChange.fire({811extensionHostKind: processManager.kind,812isResponsive: responsiveState === ResponsiveState.Responsive,813getInspectListener: (tryEnableInspector: boolean) => {814return processManager.getInspectPort(tryEnableInspector);815}816});817}));818return [processManager, disposableStore];819}820821protected _doCreateExtensionHostManager(extensionHost: IExtensionHost, initialActivationEvents: string[]): IExtensionHostManager {822const internalExtensionService = this._acquireInternalAPI(extensionHost);823if (extensionHost.startup === ExtensionHostStartup.LazyAutoStart) {824return this._instantiationService.createInstance(LazyCreateExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);825}826return this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);827}828829private _onExtensionHostCrashOrExit(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {830831// Unexpected termination832const isExtensionDevHost = parseExtensionDevOptions(this._environmentService).isExtensionDevHost;833if (!isExtensionDevHost) {834this._onExtensionHostCrashed(extensionHost, code, signal);835return;836}837838this._onExtensionHostExit(code);839}840841protected _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {842console.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. Code: ${code}, Signal: ${signal}`);843if (extensionHost.kind === ExtensionHostKind.LocalProcess) {844this._doStopExtensionHosts();845} else if (extensionHost.kind === ExtensionHostKind.Remote) {846if (signal) {847this._onRemoteExtensionHostCrashed(extensionHost, signal);848}849this._extensionHostManagers.stopOne(extensionHost);850}851}852853private _getExtensionHostExitInfoWithTimeout(reconnectionToken: string): Promise<IExtensionHostExitInfo | null> {854return new Promise((resolve, reject) => {855const timeoutHandle = setTimeout(() => {856reject(new Error('getExtensionHostExitInfo timed out'));857}, 2000);858this._remoteAgentService.getExtensionHostExitInfo(reconnectionToken).then(859(r) => {860clearTimeout(timeoutHandle);861resolve(r);862},863reject864);865});866}867868private async _onRemoteExtensionHostCrashed(extensionHost: IExtensionHostManager, reconnectionToken: string): Promise<void> {869try {870const info = await this._getExtensionHostExitInfoWithTimeout(reconnectionToken);871if (info) {872this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly with code ${info.code}.`);873}874875this._logExtensionHostCrash(extensionHost);876this._remoteCrashTracker.registerCrash();877878if (this._remoteCrashTracker.shouldAutomaticallyRestart()) {879this._logService.info(`Automatically restarting the remote extension host.`);880this._notificationService.status(nls.localize('extensionService.autoRestart', "The remote extension host terminated unexpectedly. Restarting..."), { hideAfter: 5000 });881this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));882} else {883this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Remote Extension host terminated unexpectedly 3 times within the last 5 minutes."),884[{885label: nls.localize('restart', "Restart Remote Extension Host"),886run: () => {887this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));888}889}]890);891}892} catch (err) {893// maybe this wasn't an extension host crash and it was a permanent disconnection894}895}896897protected _logExtensionHostCrash(extensionHost: IExtensionHostManager): void {898899const activatedExtensions: ExtensionIdentifier[] = [];900for (const extensionStatus of this._extensionStatus.values()) {901if (extensionStatus.activationStarted && extensionHost.containsExtension(extensionStatus.id)) {902activatedExtensions.push(extensionStatus.id);903}904}905906if (activatedExtensions.length > 0) {907this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. The following extensions were running: ${activatedExtensions.map(id => id.value).join(', ')}`);908} else {909this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. No extensions were activated.`);910}911}912913public async startExtensionHosts(updates?: { toAdd: IExtension[]; toRemove: string[] }): Promise<void> {914await this._doStopExtensionHosts();915916if (updates) {917await this._handleDeltaExtensions(new DeltaExtensionsQueueItem(updates.toAdd, updates.toRemove));918}919920const lock = await this._registry.acquireLock('startExtensionHosts');921try {922this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));923this._startOnDemandExtensionHosts();924925const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);926await Promise.all(localProcessExtensionHosts.map(extHost => extHost.ready()));927} finally {928lock.dispose();929}930}931932private _startOnDemandExtensionHosts(): void {933const snapshot = this._registry.getSnapshot();934for (const extHostManager of this._extensionHostManagers) {935if (extHostManager.startup !== ExtensionHostStartup.EagerAutoStart) {936const extensions = this._runningLocations.filterByExtensionHostManager(snapshot.extensions, extHostManager);937extHostManager.start(snapshot.versionId, snapshot.extensions, extensions.map(extension => extension.identifier));938}939}940}941942//#endregion943944//#region IExtensionService945946public activateByEvent(activationEvent: string, activationKind: ActivationKind = ActivationKind.Normal): Promise<void> {947if (this._installedExtensionsReady.isOpen()) {948// Extensions have been scanned and interpreted949950// Record the fact that this activationEvent was requested (in case of a restart)951this._allRequestedActivateEvents.add(activationEvent);952953if (!this._registry.containsActivationEvent(activationEvent)) {954// There is no extension that is interested in this activation event955return NO_OP_VOID_PROMISE;956}957958return this._activateByEvent(activationEvent, activationKind);959} else {960// Extensions have not been scanned yet.961962// Record the fact that this activationEvent was requested (in case of a restart)963this._allRequestedActivateEvents.add(activationEvent);964965if (activationKind === ActivationKind.Immediate) {966// Do not wait for the normal start-up of the extension host(s)967return this._activateByEvent(activationEvent, activationKind);968}969970return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent, activationKind));971}972}973974private _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {975const result = Promise.all(976this._extensionHostManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent, activationKind))977).then(() => { });978this._onWillActivateByEvent.fire({979event: activationEvent,980activation: result981});982return result;983}984985public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {986return this._activateById(extensionId, reason);987}988989public activationEventIsDone(activationEvent: string): boolean {990if (!this._installedExtensionsReady.isOpen()) {991return false;992}993if (!this._registry.containsActivationEvent(activationEvent)) {994// There is no extension that is interested in this activation event995return true;996}997return this._extensionHostManagers.every(manager => manager.activationEventIsDone(activationEvent));998}9991000public whenInstalledExtensionsRegistered(): Promise<boolean> {1001return this._installedExtensionsReady.wait();1002}10031004get extensions(): IExtensionDescription[] {1005return this._registry.getAllExtensionDescriptions();1006}10071008protected _getExtensionRegistrySnapshotWhenReady(): Promise<ExtensionDescriptionRegistrySnapshot> {1009return this._installedExtensionsReady.wait().then(() => this._registry.getSnapshot());1010}10111012public getExtension(id: string): Promise<IExtensionDescription | undefined> {1013return this._installedExtensionsReady.wait().then(() => {1014return this._registry.getExtensionDescription(id);1015});1016}10171018public readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> {1019return this._installedExtensionsReady.wait().then(() => {1020const availableExtensions = this._registry.getAllExtensionDescriptions();10211022const result: ExtensionPointContribution<T>[] = [];1023for (const desc of availableExtensions) {1024if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {1025result.push(new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes] as T));1026}1027}10281029return result;1030});1031}10321033public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {1034const result: { [id: string]: IExtensionsStatus } = Object.create(null);1035if (this._registry) {1036const extensions = this._registry.getAllExtensionDescriptions();1037for (const extension of extensions) {1038const extensionStatus = this._extensionStatus.get(extension.identifier);1039result[extension.identifier.value] = {1040id: extension.identifier,1041messages: extensionStatus?.messages ?? [],1042activationStarted: extensionStatus?.activationStarted ?? false,1043activationTimes: extensionStatus?.activationTimes ?? undefined,1044runtimeErrors: extensionStatus?.runtimeErrors ?? [],1045runningLocation: this._runningLocations.getRunningLocation(extension.identifier),1046};1047}1048}1049return result;1050}10511052public async getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<IExtensionInspectInfo[]> {1053const result = await Promise.all(1054this._getExtensionHostManagers(extensionHostKind).map(async extHost => {1055let portInfo = await extHost.getInspectPort(tryEnableInspector);1056if (portInfo !== undefined) {1057portInfo = { ...portInfo, devtoolsLabel: extHost.friendyName };1058}1059return portInfo;1060})1061);1062// remove 0s:1063return result.filter(isDefined);1064}10651066public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {1067await this._extensionHostManagers1068.map(manager => manager.setRemoteEnvironment(env));1069}10701071//#endregion10721073// --- impl10741075private _safeInvokeIsEnabled(extension: IExtension): boolean {1076try {1077return this._extensionEnablementService.isEnabled(extension);1078} catch (err) {1079return false;1080}1081}10821083private _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[], onlyResolverExtensionPoints: boolean): void {1084const affectedExtensionPoints: { [extPointName: string]: boolean } = Object.create(null);1085for (const extensionDescription of affectedExtensions) {1086if (extensionDescription.contributes) {1087for (const extPointName in extensionDescription.contributes) {1088if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) {1089affectedExtensionPoints[extPointName] = true;1090}1091}1092}1093}10941095const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg);1096const availableExtensions = this._registry.getAllExtensionDescriptions();1097const extensionPoints = ExtensionsRegistry.getExtensionPoints();1098perf.mark(onlyResolverExtensionPoints ? 'code/willHandleResolverExtensionPoints' : 'code/willHandleExtensionPoints');1099for (const extensionPoint of extensionPoints) {1100if (affectedExtensionPoints[extensionPoint.name] && (!onlyResolverExtensionPoints || extensionPoint.canHandleResolver)) {1101perf.mark(`code/willHandleExtensionPoint/${extensionPoint.name}`);1102AbstractExtensionService._handleExtensionPoint(extensionPoint, availableExtensions, messageHandler);1103perf.mark(`code/didHandleExtensionPoint/${extensionPoint.name}`);1104}1105}1106perf.mark(onlyResolverExtensionPoints ? 'code/didHandleResolverExtensionPoints' : 'code/didHandleExtensionPoints');1107}11081109private _getOrCreateExtensionStatus(extensionId: ExtensionIdentifier): ExtensionStatus {1110if (!this._extensionStatus.has(extensionId)) {1111this._extensionStatus.set(extensionId, new ExtensionStatus(extensionId));1112}1113return this._extensionStatus.get(extensionId)!;1114}11151116private _handleExtensionPointMessage(msg: IMessage) {1117const extensionStatus = this._getOrCreateExtensionStatus(msg.extensionId);1118extensionStatus.addMessage(msg);11191120const extension = this._registry.getExtensionDescription(msg.extensionId);1121const strMsg = `[${msg.extensionId.value}]: ${msg.message}`;11221123if (msg.type === Severity.Error) {1124if (extension && extension.isUnderDevelopment) {1125// This message is about the extension currently being developed1126this._notificationService.notify({ severity: Severity.Error, message: strMsg });1127}1128this._logService.error(strMsg);1129} else if (msg.type === Severity.Warning) {1130if (extension && extension.isUnderDevelopment) {1131// This message is about the extension currently being developed1132this._notificationService.notify({ severity: Severity.Warning, message: strMsg });1133}1134this._logService.warn(strMsg);1135} else {1136this._logService.info(strMsg);1137}11381139if (msg.extensionId && this._environmentService.isBuilt && !this._environmentService.isExtensionDevelopment) {1140const { type, extensionId, extensionPointId, message } = msg;1141type ExtensionsMessageClassification = {1142owner: 'alexdima';1143comment: 'A validation message for an extension';1144type: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Severity of problem.' };1145extensionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the extension that has a problem.' };1146extensionPointId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The extension point that has a problem.' };1147message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The message of the problem.' };1148};1149type ExtensionsMessageEvent = {1150type: Severity;1151extensionId: string;1152extensionPointId: string;1153message: string;1154};1155this._telemetryService.publicLog2<ExtensionsMessageEvent, ExtensionsMessageClassification>('extensionsMessage', {1156type, extensionId: extensionId.value, extensionPointId, message1157});1158}1159}11601161private static _handleExtensionPoint<T extends IExtensionContributions[keyof IExtensionContributions]>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {1162const users: IExtensionPointUser<T>[] = [];1163for (const desc of availableExtensions) {1164if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {1165users.push({1166description: desc,1167value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes] as T,1168collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)1169});1170}1171}1172extensionPoint.acceptUsers(users);1173}11741175//#region Called by extension host11761177private _acquireInternalAPI(extensionHost: IExtensionHost): IInternalExtensionService {1178return {1179_activateById: (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> => {1180return this._activateById(extensionId, reason);1181},1182_onWillActivateExtension: (extensionId: ExtensionIdentifier): void => {1183return this._onWillActivateExtension(extensionId, extensionHost.runningLocation);1184},1185_onDidActivateExtension: (extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void => {1186return this._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason);1187},1188_onDidActivateExtensionError: (extensionId: ExtensionIdentifier, error: Error): void => {1189return this._onDidActivateExtensionError(extensionId, error);1190},1191_onExtensionRuntimeError: (extensionId: ExtensionIdentifier, err: Error): void => {1192return this._onExtensionRuntimeError(extensionId, err);1193}1194};1195}11961197public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {1198const results = await Promise.all(1199this._extensionHostManagers.map(manager => manager.activate(extensionId, reason))1200);1201const activated = results.some(e => e);1202if (!activated) {1203throw new Error(`Unknown extension ${extensionId.value}`);1204}1205}12061207private _onWillActivateExtension(extensionId: ExtensionIdentifier, runningLocation: ExtensionRunningLocation): void {1208this._runningLocations.set(extensionId, runningLocation);1209const extensionStatus = this._getOrCreateExtensionStatus(extensionId);1210extensionStatus.onWillActivate();1211}12121213private _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void {1214const extensionStatus = this._getOrCreateExtensionStatus(extensionId);1215extensionStatus.setActivationTimes(new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason));1216this._onDidChangeExtensionsStatus.fire([extensionId]);1217}12181219private _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void {1220type ExtensionActivationErrorClassification = {1221owner: 'alexdima';1222comment: 'An extension failed to activate';1223extensionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the extension.' };1224error: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'The error message.' };1225};1226type ExtensionActivationErrorEvent = {1227extensionId: string;1228error: string;1229};1230this._telemetryService.publicLog2<ExtensionActivationErrorEvent, ExtensionActivationErrorClassification>('extensionActivationError', {1231extensionId: extensionId.value,1232error: error.message1233});1234}12351236private _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void {1237const extensionStatus = this._getOrCreateExtensionStatus(extensionId);1238extensionStatus.addRuntimeError(err);1239this._onDidChangeExtensionsStatus.fire([extensionId]);1240}12411242//#endregion12431244protected abstract _resolveExtensions(): AsyncIterable<ResolvedExtensions>;1245protected abstract _onExtensionHostExit(code: number): Promise<void>;1246protected abstract _resolveAuthority(remoteAuthority: string): Promise<ResolverResult>;1247}12481249class ExtensionHostCollection extends Disposable {12501251private _extensionHostManagers: ExtensionHostManagerData[] = [];12521253public override dispose() {1254for (let i = this._extensionHostManagers.length - 1; i >= 0; i--) {1255const manager = this._extensionHostManagers[i];1256manager.extensionHost.disconnect();1257manager.dispose();1258}1259this._extensionHostManagers = [];1260super.dispose();1261}12621263public add(extensionHostManager: IExtensionHostManager, disposableStore: DisposableStore): void {1264this._extensionHostManagers.push(new ExtensionHostManagerData(extensionHostManager, disposableStore));1265}12661267public async stopAllInReverse(): Promise<void> {1268// See https://github.com/microsoft/vscode/issues/1522041269// Dispose extension hosts in reverse creation order because the local extension host1270// might be critical in sustaining a connection to the remote extension host1271for (let i = this._extensionHostManagers.length - 1; i >= 0; i--) {1272const manager = this._extensionHostManagers[i];1273await manager.extensionHost.disconnect();1274manager.dispose();1275}1276this._extensionHostManagers = [];1277}12781279public async stopOne(extensionHostManager: IExtensionHostManager): Promise<void> {1280const index = this._extensionHostManagers.findIndex(el => el.extensionHost === extensionHostManager);1281if (index >= 0) {1282this._extensionHostManagers.splice(index, 1);1283await extensionHostManager.disconnect();1284extensionHostManager.dispose();1285}1286}12871288public getByKind(kind: ExtensionHostKind): IExtensionHostManager[] {1289return this.filter(el => el.kind === kind);1290}12911292public getByRunningLocation(runningLocation: ExtensionRunningLocation): IExtensionHostManager | null {1293for (const el of this._extensionHostManagers) {1294if (el.extensionHost.representsRunningLocation(runningLocation)) {1295return el.extensionHost;1296}1297}1298return null;1299}13001301*[Symbol.iterator]() {1302for (const extensionHostManager of this._extensionHostManagers) {1303yield extensionHostManager.extensionHost;1304}1305}13061307public map<T>(callback: (extHostManager: IExtensionHostManager) => T): T[] {1308return this._extensionHostManagers.map(el => callback(el.extensionHost));1309}13101311public every(callback: (extHostManager: IExtensionHostManager) => unknown): boolean {1312return this._extensionHostManagers.every(el => callback(el.extensionHost));1313}13141315public filter(callback: (extHostManager: IExtensionHostManager) => unknown): IExtensionHostManager[] {1316return this._extensionHostManagers.filter(el => callback(el.extensionHost)).map(el => el.extensionHost);1317}1318}13191320class ExtensionHostManagerData {1321constructor(1322public readonly extensionHost: IExtensionHostManager,1323public readonly disposableStore: DisposableStore1324) { }13251326public dispose(): void {1327this.disposableStore.dispose();1328this.extensionHost.dispose();1329}1330}13311332export class ResolverExtensions {1333constructor(1334public readonly extensions: IExtensionDescription[],1335) { }1336}13371338export class LocalExtensions {1339constructor(1340public readonly extensions: IExtensionDescription[],1341) { }1342}13431344export class RemoteExtensions {1345constructor(1346public readonly extensions: IExtensionDescription[],1347) { }1348}13491350export type ResolvedExtensions = ResolverExtensions | LocalExtensions | RemoteExtensions;13511352export interface IExtensionHostFactory {1353createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null;1354}13551356class DeltaExtensionsQueueItem {1357constructor(1358public readonly toAdd: IExtension[],1359public readonly toRemove: string[] | IExtension[]1360) { }1361}13621363export function isResolverExtension(extension: IExtensionDescription): boolean {1364return !!extension.activationEvents?.some(activationEvent => activationEvent.startsWith('onResolveRemoteAuthority:'));1365}13661367/**1368* @argument extensions The extensions to be checked.1369* @argument ignoreWorkspaceTrust Do not take workspace trust into account.1370*/1371export function checkEnabledAndProposedAPI(logService: ILogService, extensionEnablementService: IWorkbenchExtensionEnablementService, extensionsProposedApi: ExtensionsProposedApi, extensions: IExtensionDescription[], ignoreWorkspaceTrust: boolean): IExtensionDescription[] {1372// enable or disable proposed API per extension1373extensionsProposedApi.updateEnabledApiProposals(extensions);13741375// keep only enabled extensions1376return filterEnabledExtensions(logService, extensionEnablementService, extensions, ignoreWorkspaceTrust);1377}13781379/**1380* Return the subset of extensions that are enabled.1381* @argument ignoreWorkspaceTrust Do not take workspace trust into account.1382*/1383export function filterEnabledExtensions(logService: ILogService, extensionEnablementService: IWorkbenchExtensionEnablementService, extensions: IExtensionDescription[], ignoreWorkspaceTrust: boolean): IExtensionDescription[] {1384const enabledExtensions: IExtensionDescription[] = [], extensionsToCheck: IExtensionDescription[] = [], mappedExtensions: IExtension[] = [];1385for (const extension of extensions) {1386if (extension.isUnderDevelopment) {1387// Never disable extensions under development1388enabledExtensions.push(extension);1389} else {1390extensionsToCheck.push(extension);1391mappedExtensions.push(toExtension(extension));1392}1393}13941395const enablementStates = extensionEnablementService.getEnablementStates(mappedExtensions, ignoreWorkspaceTrust ? { trusted: true } : undefined);1396for (let index = 0; index < enablementStates.length; index++) {1397if (extensionEnablementService.isEnabledEnablementState(enablementStates[index])) {1398enabledExtensions.push(extensionsToCheck[index]);1399} else {1400if (isCI) {1401logService.info(`filterEnabledExtensions: extension '${extensionsToCheck[index].identifier.value}' is disabled`);1402}1403}1404}14051406return enabledExtensions;1407}14081409/**1410* @argument extension The extension to be checked.1411* @argument ignoreWorkspaceTrust Do not take workspace trust into account.1412*/1413export function extensionIsEnabled(logService: ILogService, extensionEnablementService: IWorkbenchExtensionEnablementService, extension: IExtensionDescription, ignoreWorkspaceTrust: boolean): boolean {1414return filterEnabledExtensions(logService, extensionEnablementService, [extension], ignoreWorkspaceTrust).includes(extension);1415}14161417function includes(extensions: IExtensionDescription[], identifier: ExtensionIdentifier): boolean {1418for (const extension of extensions) {1419if (ExtensionIdentifier.equals(extension.identifier, identifier)) {1420return true;1421}1422}1423return false;1424}14251426export class ExtensionStatus {14271428private readonly _messages: IMessage[] = [];1429public get messages(): IMessage[] {1430return this._messages;1431}14321433private _activationTimes: ActivationTimes | null = null;1434public get activationTimes(): ActivationTimes | null {1435return this._activationTimes;1436}14371438private _runtimeErrors: Error[] = [];1439public get runtimeErrors(): Error[] {1440return this._runtimeErrors;1441}14421443private _activationStarted: boolean = false;1444public get activationStarted(): boolean {1445return this._activationStarted;1446}14471448constructor(1449public readonly id: ExtensionIdentifier,1450) { }14511452public clearRuntimeStatus(): void {1453this._activationStarted = false;1454this._activationTimes = null;1455this._runtimeErrors = [];1456}14571458public addMessage(msg: IMessage): void {1459this._messages.push(msg);1460}14611462public setActivationTimes(activationTimes: ActivationTimes) {1463this._activationTimes = activationTimes;1464}14651466public addRuntimeError(err: Error): void {1467this._runtimeErrors.push(err);1468}14691470public onWillActivate() {1471this._activationStarted = true;1472}1473}14741475interface IExtensionHostCrashInfo {1476timestamp: number;1477}14781479export class ExtensionHostCrashTracker {14801481private static _TIME_LIMIT = 5 * 60 * 1000; // 5 minutes1482private static _CRASH_LIMIT = 3;14831484private readonly _recentCrashes: IExtensionHostCrashInfo[] = [];14851486private _removeOldCrashes(): void {1487const limit = Date.now() - ExtensionHostCrashTracker._TIME_LIMIT;1488while (this._recentCrashes.length > 0 && this._recentCrashes[0].timestamp < limit) {1489this._recentCrashes.shift();1490}1491}14921493public registerCrash(): void {1494this._removeOldCrashes();1495this._recentCrashes.push({ timestamp: Date.now() });1496}14971498public shouldAutomaticallyRestart(): boolean {1499this._removeOldCrashes();1500return (this._recentCrashes.length < ExtensionHostCrashTracker._CRASH_LIMIT);1501}1502}15031504/**1505* This can run correctly only on the renderer process because that is the only place1506* where all extension points and all implicit activation events generators are known.1507*/1508export class ImplicitActivationAwareReader implements IActivationEventsReader {1509public readActivationEvents(extensionDescription: IExtensionDescription): string[] {1510return ImplicitActivationEvents.readActivationEvents(extensionDescription);1511}1512}15131514class ActivationFeatureMarkdowneRenderer extends Disposable implements IExtensionFeatureMarkdownRenderer {15151516readonly type = 'markdown';15171518shouldRender(manifest: IExtensionManifest): boolean {1519return !!manifest.activationEvents;1520}15211522render(manifest: IExtensionManifest): IRenderedData<IMarkdownString> {1523const activationEvents = manifest.activationEvents || [];1524const data = new MarkdownString();1525if (activationEvents.length) {1526for (const activationEvent of activationEvents) {1527data.appendMarkdown(`- \`${activationEvent}\`\n`);1528}1529}1530return {1531data,1532dispose: () => { }1533};1534}1535}15361537Registry.as<IExtensionFeaturesRegistry>(ExtensionFeaturesExtensions.ExtensionFeaturesRegistry).registerExtensionFeature({1538id: 'activationEvents',1539label: nls.localize('activation', "Activation Events"),1540access: {1541canToggle: false1542},1543renderer: new SyncDescriptor(ActivationFeatureMarkdowneRenderer),1544});154515461547