Path: blob/main/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
5241 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 _pendingRemoteActivationEvents = new Set<string>();90private readonly _runningLocations: ExtensionRunningLocationTracker;91private readonly _remoteCrashTracker = new ExtensionHostCrashTracker();9293private _deltaExtensionsQueue: DeltaExtensionsQueueItem[] = [];94private _inHandleDeltaExtensions = false;9596private readonly _extensionHostManagers = this._register(new ExtensionHostCollection());9798private _resolveAuthorityAttempt: number = 0;99100constructor(101options: { hasLocalProcess: boolean; allowRemoteExtensionsInLocalWebWorker: boolean },102private readonly _extensionsProposedApi: ExtensionsProposedApi,103private readonly _extensionHostFactory: IExtensionHostFactory,104private readonly _extensionHostKindPicker: IExtensionHostKindPicker,105@IInstantiationService protected readonly _instantiationService: IInstantiationService,106@INotificationService protected readonly _notificationService: INotificationService,107@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,108@ITelemetryService protected readonly _telemetryService: ITelemetryService,109@IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,110@IFileService protected readonly _fileService: IFileService,111@IProductService protected readonly _productService: IProductService,112@IWorkbenchExtensionManagementService protected readonly _extensionManagementService: IWorkbenchExtensionManagementService,113@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,114@IConfigurationService protected readonly _configurationService: IConfigurationService,115@IExtensionManifestPropertiesService private readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService,116@ILogService protected readonly _logService: ILogService,117@IRemoteAgentService protected readonly _remoteAgentService: IRemoteAgentService,118@IRemoteExtensionsScannerService protected readonly _remoteExtensionsScannerService: IRemoteExtensionsScannerService,119@ILifecycleService private readonly _lifecycleService: ILifecycleService,120@IRemoteAuthorityResolverService protected readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,121@IDialogService private readonly _dialogService: IDialogService,122) {123super();124125this._hasLocalProcess = options.hasLocalProcess;126this._allowRemoteExtensionsInLocalWebWorker = options.allowRemoteExtensionsInLocalWebWorker;127128// help the file service to activate providers by activating extensions by file system event129this._register(this._fileService.onWillActivateFileSystemProvider(e => {130if (e.scheme !== Schemas.vscodeRemote) {131e.join(this.activateByEvent(`onFileSystem:${e.scheme}`));132}133}));134135this._runningLocations = new ExtensionRunningLocationTracker(136this._registry,137this._extensionHostKindPicker,138this._environmentService,139this._configurationService,140this._logService,141this._extensionManifestPropertiesService142);143144this._register(this._extensionEnablementService.onEnablementChanged((extensions) => {145const toAdd: IExtension[] = [];146const toRemove: IExtension[] = [];147for (const extension of extensions) {148if (this._safeInvokeIsEnabled(extension)) {149// an extension has been enabled150toAdd.push(extension);151} else {152// an extension has been disabled153toRemove.push(extension);154}155}156if (isCI) {157this._logService.info(`AbstractExtensionService.onEnablementChanged fired for ${extensions.map(e => e.identifier.id).join(', ')}`);158}159this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove));160}));161162this._register(this._extensionManagementService.onDidChangeProfile(({ added, removed }) => {163if (added.length || removed.length) {164if (isCI) {165this._logService.info(`AbstractExtensionService.onDidChangeProfile fired`);166}167this._handleDeltaExtensions(new DeltaExtensionsQueueItem(added, removed));168}169}));170171this._register(this._extensionManagementService.onDidEnableExtensions(extensions => {172if (extensions.length) {173if (isCI) {174this._logService.info(`AbstractExtensionService.onDidEnableExtensions fired`);175}176this._handleDeltaExtensions(new DeltaExtensionsQueueItem(extensions, []));177}178}));179180this._register(this._extensionManagementService.onDidInstallExtensions((result) => {181const extensions: IExtension[] = [];182const toRemove: string[] = [];183for (const { local, operation } of result) {184if (local && local.isValid && operation !== InstallOperation.Migrate && this._safeInvokeIsEnabled(local)) {185extensions.push(local);186if (operation === InstallOperation.Update) {187toRemove.push(local.identifier.id);188}189}190}191if (extensions.length) {192if (isCI) {193this._logService.info(`AbstractExtensionService.onDidInstallExtensions fired for ${extensions.map(e => e.identifier.id).join(', ')}`);194}195this._handleDeltaExtensions(new DeltaExtensionsQueueItem(extensions, toRemove));196}197}));198199this._register(this._extensionManagementService.onDidUninstallExtension((event) => {200if (!event.error) {201// an extension has been uninstalled202if (isCI) {203this._logService.info(`AbstractExtensionService.onDidUninstallExtension fired for ${event.identifier.id}`);204}205this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id]));206}207}));208209this._register(this._lifecycleService.onWillShutdown(event => {210if (this._remoteAgentService.getConnection()) {211event.join(async () => {212// We need to disconnect the management connection before killing the local extension host.213// Otherwise, the local extension host might terminate the underlying tunnel before the214// management connection has a chance to send its disconnection message.215try {216await this._remoteAgentService.endConnection();217await this._doStopExtensionHosts();218this._remoteAgentService.getConnection()?.dispose();219} catch {220this._logService.warn('Error while disconnecting remote agent');221}222}, {223id: 'join.disconnectRemote',224label: nls.localize('disconnectRemote', "Disconnect Remote Agent"),225order: WillShutdownJoinerOrder.Last // after others have joined that might depend on a remote connection226});227} else {228event.join(this._doStopExtensionHosts(), {229id: 'join.stopExtensionHosts',230label: nls.localize('stopExtensionHosts', "Stopping Extension Hosts"),231});232}233}));234}235236protected _getExtensionHostManagers(kind: ExtensionHostKind): IExtensionHostManager[] {237return this._extensionHostManagers.getByKind(kind);238}239240//#region deltaExtensions241242private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise<void> {243this._deltaExtensionsQueue.push(item);244if (this._inHandleDeltaExtensions) {245// Let the current item finish, the new one will be picked up246return;247}248249let lock: ExtensionDescriptionRegistryLock | null = null;250try {251this._inHandleDeltaExtensions = true;252253// wait for _initialize to finish before hanlding any delta extension events254await this._installedExtensionsReady.wait();255256lock = await this._registry.acquireLock('handleDeltaExtensions');257while (this._deltaExtensionsQueue.length > 0) {258const item = this._deltaExtensionsQueue.shift()!;259await this._deltaExtensions(lock, item.toAdd, item.toRemove);260}261} finally {262this._inHandleDeltaExtensions = false;263lock?.dispose();264}265}266267private async _deltaExtensions(lock: ExtensionDescriptionRegistryLock, _toAdd: IExtension[], _toRemove: string[] | IExtension[]): Promise<void> {268if (isCI) {269this._logService.info(`AbstractExtensionService._deltaExtensions: toAdd: [${_toAdd.map(e => e.identifier.id).join(',')}] toRemove: [${_toRemove.map(e => typeof e === 'string' ? e : e.identifier.id).join(',')}]`);270}271let toRemove: IExtensionDescription[] = [];272for (let i = 0, len = _toRemove.length; i < len; i++) {273const extensionOrId = _toRemove[i];274const extensionId = (typeof extensionOrId === 'string' ? extensionOrId : extensionOrId.identifier.id);275const extension = (typeof extensionOrId === 'string' ? null : extensionOrId);276const extensionDescription = this._registry.getExtensionDescription(extensionId);277if (!extensionDescription) {278// ignore disabling/uninstalling an extension which is not running279continue;280}281282if (extension && extensionDescription.extensionLocation.scheme !== extension.location.scheme) {283// this event is for a different extension than mine (maybe for the local extension, while I have the remote extension)284continue;285}286287if (!this.canRemoveExtension(extensionDescription)) {288// uses non-dynamic extension point or is activated289continue;290}291292toRemove.push(extensionDescription);293}294295const toAdd: IExtensionDescription[] = [];296for (let i = 0, len = _toAdd.length; i < len; i++) {297const extension = _toAdd[i];298299const extensionDescription = toExtensionDescription(extension, false);300if (!extensionDescription) {301// could not scan extension...302continue;303}304305if (!this._canAddExtension(extensionDescription, toRemove)) {306continue;307}308309toAdd.push(extensionDescription);310}311312if (toAdd.length === 0 && toRemove.length === 0) {313return;314}315316// Update the local registry317const result = this._registry.deltaExtensions(lock, toAdd, toRemove.map(e => e.identifier));318this._onDidChangeExtensions.fire({ added: toAdd, removed: toRemove });319320toRemove = toRemove.concat(result.removedDueToLooping);321if (result.removedDueToLooping.length > 0) {322this._notificationService.notify({323severity: Severity.Error,324message: nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))325});326}327328// enable or disable proposed API per extension329this._extensionsProposedApi.updateEnabledApiProposals(toAdd);330331// Update extension points332this._doHandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove), false);333334// Update the extension host335await this._updateExtensionsOnExtHosts(result.versionId, toAdd, toRemove.map(e => e.identifier));336337for (let i = 0; i < toAdd.length; i++) {338this._activateAddedExtensionIfNeeded(toAdd[i]);339}340}341342private async _updateExtensionsOnExtHosts(versionId: number, toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {343const removedRunningLocation = this._runningLocations.deltaExtensions(toAdd, toRemove);344const promises = this._extensionHostManagers.map(345extHostManager => this._updateExtensionsOnExtHost(extHostManager, versionId, toAdd, toRemove, removedRunningLocation)346);347await Promise.all(promises);348}349350private async _updateExtensionsOnExtHost(extensionHostManager: IExtensionHostManager, versionId: number, toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[], removedRunningLocation: ExtensionIdentifierMap<ExtensionRunningLocation | null>): Promise<void> {351const myToAdd = this._runningLocations.filterByExtensionHostManager(toAdd, extensionHostManager);352const myToRemove = filterExtensionIdentifiers(toRemove, removedRunningLocation, extRunningLocation => extensionHostManager.representsRunningLocation(extRunningLocation));353const addActivationEvents = ImplicitActivationEvents.createActivationEventsMap(toAdd);354if (isCI) {355const printExtIds = (extensions: IExtensionDescription[]) => extensions.map(e => e.identifier.value).join(',');356const printIds = (extensions: ExtensionIdentifier[]) => extensions.map(e => e.value).join(',');357this._logService.info(`AbstractExtensionService: Calling deltaExtensions: toRemove: [${printIds(toRemove)}], toAdd: [${printExtIds(toAdd)}], myToRemove: [${printIds(myToRemove)}], myToAdd: [${printExtIds(myToAdd)}],`);358}359await extensionHostManager.deltaExtensions({ versionId, toRemove, toAdd, addActivationEvents, myToRemove, myToAdd: myToAdd.map(extension => extension.identifier) });360}361362public canAddExtension(extension: IExtensionDescription): boolean {363return this._canAddExtension(extension, []);364}365366private _canAddExtension(extension: IExtensionDescription, extensionsBeingRemoved: IExtensionDescription[]): boolean {367// (Also check for renamed extensions)368const existing = this._registry.getExtensionDescriptionByIdOrUUID(extension.identifier, extension.id);369if (existing) {370// This extension is already known (most likely at a different version)371// so it cannot be added again unless it is removed first372const isBeingRemoved = extensionsBeingRemoved.some((extensionDescription) => ExtensionIdentifier.equals(extension.identifier, extensionDescription.identifier));373if (!isBeingRemoved) {374return false;375}376}377378const extensionKinds = this._runningLocations.readExtensionKinds(extension);379const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote;380const extensionHostKind = this._extensionHostKindPicker.pickExtensionHostKind(extension.identifier, extensionKinds, !isRemote, isRemote, ExtensionRunningPreference.None);381if (extensionHostKind === null) {382return false;383}384385return true;386}387388public canRemoveExtension(extension: IExtensionDescription): boolean {389const extensionDescription = this._registry.getExtensionDescription(extension.identifier);390if (!extensionDescription) {391// Can't remove an extension that is unknown!392return false;393}394395if (this._extensionStatus.get(extensionDescription.identifier)?.activationStarted) {396// Extension is running, cannot remove it safely397return false;398}399400return true;401}402403private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise<void> {404let shouldActivateReason: string | null = null;405let hasWorkspaceContains = false;406const activationEvents = this._activationEventReader.readActivationEvents(extensionDescription);407for (const activationEvent of activationEvents) {408if (this._allRequestedActivateEvents.has(activationEvent)) {409// This activation event was fired before the extension was added410shouldActivateReason = activationEvent;411break;412}413414if (activationEvent === '*') {415shouldActivateReason = activationEvent;416break;417}418419if (/^workspaceContains/.test(activationEvent)) {420hasWorkspaceContains = true;421}422423if (activationEvent === 'onStartupFinished') {424shouldActivateReason = activationEvent;425break;426}427}428429if (!shouldActivateReason && hasWorkspaceContains) {430const workspace = await this._contextService.getCompleteWorkspace();431const forceUsingSearch = !!this._environmentService.remoteAuthority;432const host: IWorkspaceContainsActivationHost = {433logService: this._logService,434folders: workspace.folders.map(folder => folder.uri),435forceUsingSearch: forceUsingSearch,436exists: (uri) => this._fileService.exists(uri),437checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token))438};439440const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription);441if (result) {442shouldActivateReason = result.activationEvent;443}444}445446if (shouldActivateReason) {447await Promise.all(448this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason }))449);450}451}452453//#endregion454455private _initializePromise: Promise<void> | null = null;456protected _initializeIfNeeded(): Promise<void> | null {457if (!this._initializePromise) {458this._initializePromise = this._initialize();459}460return this._initializePromise;461}462463protected async _initialize(): Promise<void> {464perf.mark('code/willLoadExtensions');465this._startExtensionHostsIfNecessary(true, []);466467const lock = await this._registry.acquireLock('_initialize');468try {469await this._resolveAndProcessExtensions(lock);470// Start extension hosts which are not automatically started471this._startOnDemandExtensionHosts();472} finally {473lock.dispose();474}475476this._releaseBarrier();477perf.mark('code/didLoadExtensions');478479// Activate deferred remote events now that remote hosts are starting480// This is done after the barrier is released to avoid blocking initialization481this._activateDeferredRemoteEvents();482483await this._handleExtensionTests();484}485486private async _activateDeferredRemoteEvents(): Promise<void> {487if (this._pendingRemoteActivationEvents.size === 0) {488return;489}490491const remoteExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.Remote);492if (remoteExtensionHosts.length === 0) {493this._pendingRemoteActivationEvents.clear();494return;495}496497// Wait for remote extension hosts to be ready498await Promise.all(remoteExtensionHosts.map(extHost => extHost.ready()));499500// Replay deferred activation events on remote hosts501for (const activationEvent of this._pendingRemoteActivationEvents) {502const result = Promise.all(503remoteExtensionHosts.map(extHostManager => extHostManager.activateByEvent(activationEvent, ActivationKind.Normal))504).then(() => { });505this._onWillActivateByEvent.fire({506event: activationEvent,507activation: result,508activationKind: ActivationKind.Normal509});510}511512this._pendingRemoteActivationEvents.clear();513}514515private async _resolveAndProcessExtensions(lock: ExtensionDescriptionRegistryLock,): Promise<void> {516let resolverExtensions: IExtensionDescription[] = [];517let localExtensions: IExtensionDescription[] = [];518let remoteExtensions: IExtensionDescription[] = [];519520for await (const extensions of this._resolveExtensions()) {521if (extensions instanceof ResolverExtensions) {522resolverExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, extensions.extensions, false);523this._registry.deltaExtensions(lock, resolverExtensions, []);524this._doHandleExtensionPoints(resolverExtensions, true);525}526if (extensions instanceof LocalExtensions) {527localExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, extensions.extensions, false);528}529if (extensions instanceof RemoteExtensions) {530remoteExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, extensions.extensions, false);531}532}533534// `initializeRunningLocation` will look at the complete picture (e.g. an extension installed on both sides),535// takes care of duplicates and picks a running location for each extension536this._runningLocations.initializeRunningLocation(localExtensions, remoteExtensions);537538this._startExtensionHostsIfNecessary(true, []);539540// Some remote extensions could run locally in the web worker, so store them541const remoteExtensionsThatNeedToRunLocally = (this._allowRemoteExtensionsInLocalWebWorker ? this._runningLocations.filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.LocalWebWorker) : []);542const localProcessExtensions = (this._hasLocalProcess ? this._runningLocations.filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalProcess) : []);543const localWebWorkerExtensions = this._runningLocations.filterByExtensionHostKind(localExtensions, ExtensionHostKind.LocalWebWorker);544remoteExtensions = this._runningLocations.filterByExtensionHostKind(remoteExtensions, ExtensionHostKind.Remote);545546// Add locally the remote extensions that need to run locally in the web worker547for (const ext of remoteExtensionsThatNeedToRunLocally) {548if (!includes(localWebWorkerExtensions, ext.identifier)) {549localWebWorkerExtensions.push(ext);550}551}552553const allExtensions = remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions);554let toAdd = allExtensions;555556if (resolverExtensions.length) {557// Add extensions that are not registered as resolvers but are in the final resolved set558toAdd = allExtensions.filter(extension => !resolverExtensions.some(e => ExtensionIdentifier.equals(e.identifier, extension.identifier) && e.extensionLocation.toString() === extension.extensionLocation.toString()));559// Remove extensions that are registered as resolvers but are not in the final resolved set560if (allExtensions.length < toAdd.length + resolverExtensions.length) {561const toRemove = resolverExtensions.filter(registered => !allExtensions.some(e => ExtensionIdentifier.equals(e.identifier, registered.identifier) && e.extensionLocation.toString() === registered.extensionLocation.toString()));562if (toRemove.length) {563this._registry.deltaExtensions(lock, [], toRemove.map(e => e.identifier));564this._doHandleExtensionPoints(toRemove, true);565}566}567}568569const result = this._registry.deltaExtensions(lock, toAdd, []);570if (result.removedDueToLooping.length > 0) {571this._notificationService.notify({572severity: Severity.Error,573message: nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))574});575}576577this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions(), false);578}579580private async _handleExtensionTests(): Promise<void> {581if (!this._environmentService.isExtensionDevelopment || !this._environmentService.extensionTestsLocationURI) {582return;583}584585const extensionHostManager = this.findTestExtensionHost(this._environmentService.extensionTestsLocationURI);586if (!extensionHostManager) {587const msg = nls.localize('extensionTestError', "No extension host found that can launch the test runner at {0}.", this._environmentService.extensionTestsLocationURI.toString());588console.error(msg);589this._notificationService.error(msg);590return;591}592593594let exitCode: number;595try {596exitCode = await extensionHostManager.extensionTestsExecute();597if (isCI) {598this._logService.info(`Extension host test runner exit code: ${exitCode}`);599}600} catch (err) {601if (isCI) {602this._logService.error(`Extension host test runner error`, err);603}604console.error(err);605exitCode = 1 /* ERROR */;606}607608this._onExtensionHostExit(exitCode);609}610611private findTestExtensionHost(testLocation: URI): IExtensionHostManager | null {612let runningLocation: ExtensionRunningLocation | null = null;613614for (const extension of this._registry.getAllExtensionDescriptions()) {615if (isEqualOrParent(testLocation, extension.extensionLocation)) {616runningLocation = this._runningLocations.getRunningLocation(extension.identifier);617break;618}619}620if (runningLocation === null) {621// not sure if we should support that, but it was possible to have an test outside an extension622623if (testLocation.scheme === Schemas.vscodeRemote) {624runningLocation = new RemoteRunningLocation();625} else {626// When a debugger attaches to the extension host, it will surface all console.log messages from the extension host,627// but not necessarily from the window. So it would be best if any errors get printed to the console of the extension host.628// That is why here we use the local process extension host even for non-file URIs629runningLocation = new LocalProcessRunningLocation(0);630}631}632if (runningLocation !== null) {633return this._extensionHostManagers.getByRunningLocation(runningLocation);634}635return null;636}637638private _releaseBarrier(): void {639this._installedExtensionsReady.open();640this._onDidRegisterExtensions.fire(undefined);641this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier));642}643644//#region remote authority resolving645646protected async _resolveAuthorityInitial(remoteAuthority: string): Promise<ResolverResult> {647const MAX_ATTEMPTS = 5;648649for (let attempt = 1; ; attempt++) {650try {651return this._resolveAuthorityWithLogging(remoteAuthority);652} catch (err) {653if (RemoteAuthorityResolverError.isNoResolverFound(err)) {654// There is no point in retrying if there is no resolver found655throw err;656}657658if (RemoteAuthorityResolverError.isNotAvailable(err)) {659// The resolver is not available and asked us to not retry660throw err;661}662663if (attempt >= MAX_ATTEMPTS) {664// Too many failed attempts, give up665throw err;666}667}668}669}670671protected async _resolveAuthorityAgain(): Promise<void> {672const remoteAuthority = this._environmentService.remoteAuthority;673if (!remoteAuthority) {674return;675}676677this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);678try {679const result = await this._resolveAuthorityWithLogging(remoteAuthority);680this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options);681} catch (err) {682this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);683}684}685686private async _resolveAuthorityWithLogging(remoteAuthority: string): Promise<ResolverResult> {687const authorityPrefix = getRemoteAuthorityPrefix(remoteAuthority);688const sw = StopWatch.create(false);689this._logService.info(`Invoking resolveAuthority(${authorityPrefix})...`);690try {691perf.mark(`code/willResolveAuthority/${authorityPrefix}`);692const result = await this._resolveAuthority(remoteAuthority);693perf.mark(`code/didResolveAuthorityOK/${authorityPrefix}`);694this._logService.info(`resolveAuthority(${authorityPrefix}) returned '${result.authority.connectTo}' after ${sw.elapsed()} ms`);695return result;696} catch (err) {697perf.mark(`code/didResolveAuthorityError/${authorityPrefix}`);698this._logService.error(`resolveAuthority(${authorityPrefix}) returned an error after ${sw.elapsed()} ms`, err);699throw err;700}701}702703protected async _resolveAuthorityOnExtensionHosts(kind: ExtensionHostKind, remoteAuthority: string): Promise<ResolverResult> {704705const extensionHosts = this._getExtensionHostManagers(kind);706if (extensionHosts.length === 0) {707// no local process extension hosts708throw new Error(`Cannot resolve authority`);709}710711this._resolveAuthorityAttempt++;712const results = await Promise.all(extensionHosts.map(extHost => extHost.resolveAuthority(remoteAuthority, this._resolveAuthorityAttempt)));713714let bestErrorResult: IResolveAuthorityErrorResult | null = null;715for (const result of results) {716if (result.type === 'ok') {717return result.value;718}719if (!bestErrorResult) {720bestErrorResult = result;721continue;722}723const bestErrorIsUnknown = (bestErrorResult.error.code === RemoteAuthorityResolverErrorCode.Unknown);724const errorIsUnknown = (result.error.code === RemoteAuthorityResolverErrorCode.Unknown);725if (bestErrorIsUnknown && !errorIsUnknown) {726bestErrorResult = result;727}728}729730// we can only reach this if there is an error731throw new RemoteAuthorityResolverError(bestErrorResult!.error.message, bestErrorResult!.error.code, bestErrorResult!.error.detail);732}733734//#endregion735736//#region Stopping / Starting / Restarting737738public async stopExtensionHosts(reason: string, auto?: boolean): Promise<boolean> {739await this._initializeIfNeeded();740return this._doStopExtensionHostsWithVeto(reason, auto);741}742743protected async _doStopExtensionHosts(): Promise<void> {744const previouslyActivatedExtensionIds: ExtensionIdentifier[] = [];745for (const extensionStatus of this._extensionStatus.values()) {746if (extensionStatus.activationStarted) {747previouslyActivatedExtensionIds.push(extensionStatus.id);748}749}750751await this._extensionHostManagers.stopAllInReverse();752for (const extensionStatus of this._extensionStatus.values()) {753extensionStatus.clearRuntimeStatus();754}755756if (previouslyActivatedExtensionIds.length > 0) {757this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);758}759}760761private async _doStopExtensionHostsWithVeto(reason: string, auto: boolean = false): Promise<boolean> {762if (auto && this._environmentService.isExtensionDevelopment) {763return false;764}765766const vetos: (boolean | Promise<boolean>)[] = [];767const vetoReasons = new Set<string>();768769this._onWillStop.fire({770reason,771auto,772veto(value, reason) {773vetos.push(value);774775if (typeof value === 'boolean') {776if (value === true) {777vetoReasons.add(reason);778}779} else {780value.then(value => {781if (value) {782vetoReasons.add(reason);783}784}).catch(error => {785vetoReasons.add(nls.localize('extensionStopVetoError', "{0} (Error: {1})", reason, toErrorMessage(error)));786});787}788}789});790791const veto = await handleVetos(vetos, error => this._logService.error(error));792if (!veto) {793await this._doStopExtensionHosts();794} else {795if (!auto) {796const vetoReasonsArray = Array.from(vetoReasons);797798this._logService.warn(`Extension host was not stopped because of veto (stop reason: ${reason}, veto reason: ${vetoReasonsArray.join(', ')})`);799800const { confirmed } = await this._dialogService.confirm({801type: Severity.Warning,802message: nls.localize('extensionStopVetoMessage', "Please confirm restart of extensions."),803detail: vetoReasonsArray.length === 1 ?804vetoReasonsArray[0] :805vetoReasonsArray.join('\n -'),806primaryButton: nls.localize('proceedAnyways', "Restart Anyway")807});808809if (confirmed) {810return true;811}812}813814}815816return !veto;817}818819private _startExtensionHostsIfNecessary(isInitialStart: boolean, initialActivationEvents: string[]): void {820const locations: ExtensionRunningLocation[] = [];821for (let affinity = 0; affinity <= this._runningLocations.maxLocalProcessAffinity; affinity++) {822locations.push(new LocalProcessRunningLocation(affinity));823}824for (let affinity = 0; affinity <= this._runningLocations.maxLocalWebWorkerAffinity; affinity++) {825locations.push(new LocalWebWorkerRunningLocation(affinity));826}827locations.push(new RemoteRunningLocation());828for (const location of locations) {829if (this._extensionHostManagers.getByRunningLocation(location)) {830// already running831continue;832}833const res = this._createExtensionHostManager(location, isInitialStart, initialActivationEvents);834if (res) {835const [extHostManager, disposableStore] = res;836this._extensionHostManagers.add(extHostManager, disposableStore);837}838}839}840841private _createExtensionHostManager(runningLocation: ExtensionRunningLocation, isInitialStart: boolean, initialActivationEvents: string[]): null | [IExtensionHostManager, DisposableStore] {842const extensionHost = this._extensionHostFactory.createExtensionHost(this._runningLocations, runningLocation, isInitialStart);843if (!extensionHost) {844return null;845}846847const processManager: IExtensionHostManager = this._doCreateExtensionHostManager(extensionHost, initialActivationEvents);848const disposableStore = new DisposableStore();849disposableStore.add(processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)));850disposableStore.add(processManager.onDidChangeResponsiveState((responsiveState) => {851this._logService.info(`Extension host (${processManager.friendyName}) is ${responsiveState === ResponsiveState.Responsive ? 'responsive' : 'unresponsive'}.`);852this._onDidChangeResponsiveChange.fire({853extensionHostKind: processManager.kind,854isResponsive: responsiveState === ResponsiveState.Responsive,855getInspectListener: (tryEnableInspector: boolean) => {856return processManager.getInspectPort(tryEnableInspector);857}858});859}));860return [processManager, disposableStore];861}862863protected _doCreateExtensionHostManager(extensionHost: IExtensionHost, initialActivationEvents: string[]): IExtensionHostManager {864const internalExtensionService = this._acquireInternalAPI(extensionHost);865if (extensionHost.startup === ExtensionHostStartup.LazyAutoStart) {866return this._instantiationService.createInstance(LazyCreateExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);867}868return this._instantiationService.createInstance(ExtensionHostManager, extensionHost, initialActivationEvents, internalExtensionService);869}870871private _onExtensionHostCrashOrExit(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {872873// Unexpected termination874const isExtensionDevHost = parseExtensionDevOptions(this._environmentService).isExtensionDevHost;875if (!isExtensionDevHost) {876this._onExtensionHostCrashed(extensionHost, code, signal);877return;878}879880this._onExtensionHostExit(code);881}882883protected _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {884console.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. Code: ${code}, Signal: ${signal}`);885if (extensionHost.kind === ExtensionHostKind.LocalProcess) {886this._doStopExtensionHosts();887} else if (extensionHost.kind === ExtensionHostKind.Remote) {888if (signal) {889this._onRemoteExtensionHostCrashed(extensionHost, signal);890}891this._extensionHostManagers.stopOne(extensionHost);892}893}894895private _getExtensionHostExitInfoWithTimeout(reconnectionToken: string): Promise<IExtensionHostExitInfo | null> {896return new Promise((resolve, reject) => {897const timeoutHandle = setTimeout(() => {898reject(new Error('getExtensionHostExitInfo timed out'));899}, 2000);900this._remoteAgentService.getExtensionHostExitInfo(reconnectionToken).then(901(r) => {902clearTimeout(timeoutHandle);903resolve(r);904},905reject906);907});908}909910private async _onRemoteExtensionHostCrashed(extensionHost: IExtensionHostManager, reconnectionToken: string): Promise<void> {911try {912const info = await this._getExtensionHostExitInfoWithTimeout(reconnectionToken);913if (info) {914this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly with code ${info.code}.`);915}916917this._logExtensionHostCrash(extensionHost);918this._remoteCrashTracker.registerCrash();919920if (this._remoteCrashTracker.shouldAutomaticallyRestart()) {921this._logService.info(`Automatically restarting the remote extension host.`);922this._notificationService.status(nls.localize('extensionService.autoRestart', "The remote extension host terminated unexpectedly. Restarting..."), { hideAfter: 5000 });923this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));924} else {925this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Remote Extension host terminated unexpectedly 3 times within the last 5 minutes."),926[{927label: nls.localize('restart', "Restart Remote Extension Host"),928run: () => {929this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));930}931}]932);933}934} catch (err) {935// maybe this wasn't an extension host crash and it was a permanent disconnection936}937}938939protected _logExtensionHostCrash(extensionHost: IExtensionHostManager): void {940941const activatedExtensions: ExtensionIdentifier[] = [];942for (const extensionStatus of this._extensionStatus.values()) {943if (extensionStatus.activationStarted && extensionHost.containsExtension(extensionStatus.id)) {944activatedExtensions.push(extensionStatus.id);945}946}947948if (activatedExtensions.length > 0) {949this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. The following extensions were running: ${activatedExtensions.map(id => id.value).join(', ')}`);950} else {951this._logService.error(`Extension host (${extensionHost.friendyName}) terminated unexpectedly. No extensions were activated.`);952}953}954955public async startExtensionHosts(updates?: { toAdd: IExtension[]; toRemove: string[] }): Promise<void> {956await this._doStopExtensionHosts();957958if (updates) {959await this._handleDeltaExtensions(new DeltaExtensionsQueueItem(updates.toAdd, updates.toRemove));960}961962const lock = await this._registry.acquireLock('startExtensionHosts');963try {964this._startExtensionHostsIfNecessary(false, Array.from(this._allRequestedActivateEvents.keys()));965this._startOnDemandExtensionHosts();966967const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);968await Promise.all(localProcessExtensionHosts.map(extHost => extHost.ready()));969} finally {970lock.dispose();971}972}973974private _startOnDemandExtensionHosts(): void {975const snapshot = this._registry.getSnapshot();976for (const extHostManager of this._extensionHostManagers) {977if (extHostManager.startup !== ExtensionHostStartup.EagerAutoStart) {978const extensions = this._runningLocations.filterByExtensionHostManager(snapshot.extensions, extHostManager);979extHostManager.start(snapshot.versionId, snapshot.extensions, extensions.map(extension => extension.identifier));980}981}982}983984//#endregion985986//#region IExtensionService987988public activateByEvent(activationEvent: string, activationKind: ActivationKind = ActivationKind.Normal): Promise<void> {989if (this._installedExtensionsReady.isOpen()) {990// Extensions have been scanned and interpreted991992// Record the fact that this activationEvent was requested (in case of a restart)993this._allRequestedActivateEvents.add(activationEvent);994995if (!this._registry.containsActivationEvent(activationEvent)) {996// There is no extension that is interested in this activation event997return NO_OP_VOID_PROMISE;998}9991000return this._activateByEvent(activationEvent, activationKind);1001} else {1002// Extensions have not been scanned yet.10031004// Record the fact that this activationEvent was requested (in case of a restart)1005this._allRequestedActivateEvents.add(activationEvent);10061007if (activationKind === ActivationKind.Immediate) {1008// Do not wait for the normal start-up of the extension host(s)10091010// Note: some callers come in so early that the extension hosts have not even been created yet.1011// Therefore we kick off the extension host creation, but without awaiting it.1012// See https://github.com/microsoft/vscode/issues/2600611013void this._initializeIfNeeded();10141015return this._activateByEvent(activationEvent, activationKind);1016}10171018return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent, activationKind));1019}1020}10211022private _activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {1023let managers: IExtensionHostManager[];1024if (activationKind === ActivationKind.Immediate) {1025// For immediate activation, only activate on local extension hosts1026// and defer remote activation until the remote host is ready1027managers = this._extensionHostManagers.filter(1028extHostManager => extHostManager.kind === ExtensionHostKind.LocalProcess || extHostManager.kind === ExtensionHostKind.LocalWebWorker1029);1030this._pendingRemoteActivationEvents.add(activationEvent);1031} else {1032managers = [...this._extensionHostManagers];1033}10341035const result = Promise.all(1036managers.map(extHostManager => extHostManager.activateByEvent(activationEvent, activationKind))1037).then(() => { });1038this._onWillActivateByEvent.fire({1039event: activationEvent,1040activation: result,1041activationKind1042});1043return result;1044}10451046public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {1047return this._activateById(extensionId, reason);1048}10491050public activationEventIsDone(activationEvent: string): boolean {1051if (!this._installedExtensionsReady.isOpen()) {1052return false;1053}1054if (!this._registry.containsActivationEvent(activationEvent)) {1055// There is no extension that is interested in this activation event1056return true;1057}1058return this._extensionHostManagers.every(manager => manager.activationEventIsDone(activationEvent));1059}10601061public whenInstalledExtensionsRegistered(): Promise<boolean> {1062return this._installedExtensionsReady.wait();1063}10641065get extensions(): IExtensionDescription[] {1066return this._registry.getAllExtensionDescriptions();1067}10681069protected _getExtensionRegistrySnapshotWhenReady(): Promise<ExtensionDescriptionRegistrySnapshot> {1070return this._installedExtensionsReady.wait().then(() => this._registry.getSnapshot());1071}10721073public getExtension(id: string): Promise<IExtensionDescription | undefined> {1074return this._installedExtensionsReady.wait().then(() => {1075return this._registry.getExtensionDescription(id);1076});1077}10781079public readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> {1080return this._installedExtensionsReady.wait().then(() => {1081const availableExtensions = this._registry.getAllExtensionDescriptions();10821083const result: ExtensionPointContribution<T>[] = [];1084for (const desc of availableExtensions) {1085if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {1086result.push(new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name as keyof typeof desc.contributes] as T));1087}1088}10891090return result;1091});1092}10931094public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {1095const result: { [id: string]: IExtensionsStatus } = Object.create(null);1096if (this._registry) {1097const extensions = this._registry.getAllExtensionDescriptions();1098for (const extension of extensions) {1099const extensionStatus = this._extensionStatus.get(extension.identifier);1100result[extension.identifier.value] = {1101id: extension.identifier,1102messages: extensionStatus?.messages ?? [],1103activationStarted: extensionStatus?.activationStarted ?? false,1104activationTimes: extensionStatus?.activationTimes ?? undefined,1105runtimeErrors: extensionStatus?.runtimeErrors ?? [],1106runningLocation: this._runningLocations.getRunningLocation(extension.identifier),1107};1108}1109}1110return result;1111}11121113public async getInspectPorts(extensionHostKind: ExtensionHostKind, tryEnableInspector: boolean): Promise<IExtensionInspectInfo[]> {1114const result = await Promise.all(1115this._getExtensionHostManagers(extensionHostKind).map(async extHost => {1116let portInfo = await extHost.getInspectPort(tryEnableInspector);1117if (portInfo !== undefined) {1118portInfo = { ...portInfo, devtoolsLabel: extHost.friendyName };1119}1120return portInfo;1121})1122);1123// remove 0s:1124return result.filter(isDefined);1125}11261127public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {1128await this._extensionHostManagers1129.map(manager => manager.setRemoteEnvironment(env));1130}11311132//#endregion11331134// --- impl11351136private _safeInvokeIsEnabled(extension: IExtension): boolean {1137try {1138return this._extensionEnablementService.isEnabled(extension);1139} catch (err) {1140return false;1141}1142}11431144private _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[], onlyResolverExtensionPoints: boolean): void {1145const affectedExtensionPoints: { [extPointName: string]: boolean } = Object.create(null);1146for (const extensionDescription of affectedExtensions) {1147if (extensionDescription.contributes) {1148for (const extPointName in extensionDescription.contributes) {1149if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) {1150affectedExtensionPoints[extPointName] = true;1151}1152}1153}1154}11551156const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg);1157const availableExtensions = this._registry.getAllExtensionDescriptions();1158const extensionPoints = ExtensionsRegistry.getExtensionPoints();1159perf.mark(onlyResolverExtensionPoints ? 'code/willHandleResolverExtensionPoints' : 'code/willHandleExtensionPoints');1160for (const extensionPoint of extensionPoints) {1161if (affectedExtensionPoints[extensionPoint.name] && (!onlyResolverExtensionPoints || extensionPoint.canHandleResolver)) {1162perf.mark(`code/willHandleExtensionPoint/${extensionPoint.name}`);1163AbstractExtensionService._handleExtensionPoint(extensionPoint, availableExtensions, messageHandler);1164perf.mark(`code/didHandleExtensionPoint/${extensionPoint.name}`);1165}1166}1167perf.mark(onlyResolverExtensionPoints ? 'code/didHandleResolverExtensionPoints' : 'code/didHandleExtensionPoints');1168}11691170private _getOrCreateExtensionStatus(extensionId: ExtensionIdentifier): ExtensionStatus {1171if (!this._extensionStatus.has(extensionId)) {1172this._extensionStatus.set(extensionId, new ExtensionStatus(extensionId));1173}1174return this._extensionStatus.get(extensionId)!;1175}11761177private _handleExtensionPointMessage(msg: IMessage) {1178const extensionStatus = this._getOrCreateExtensionStatus(msg.extensionId);1179extensionStatus.addMessage(msg);11801181const extension = this._registry.getExtensionDescription(msg.extensionId);1182const strMsg = `[${msg.extensionId.value}]: ${msg.message}`;11831184if (msg.type === Severity.Error) {1185if (extension && extension.isUnderDevelopment) {1186// This message is about the extension currently being developed1187this._notificationService.notify({ severity: Severity.Error, message: strMsg });1188}1189this._logService.error(strMsg);1190} else if (msg.type === Severity.Warning) {1191if (extension && extension.isUnderDevelopment) {1192// This message is about the extension currently being developed1193this._notificationService.notify({ severity: Severity.Warning, message: strMsg });1194}1195this._logService.warn(strMsg);1196} else {1197this._logService.info(strMsg);1198}11991200if (msg.extensionId && this._environmentService.isBuilt && !this._environmentService.isExtensionDevelopment) {1201const { type, extensionId, extensionPointId, message } = msg;1202type ExtensionsMessageClassification = {1203owner: 'alexdima';1204comment: 'A validation message for an extension';1205type: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Severity of problem.' };1206extensionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the extension that has a problem.' };1207extensionPointId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The extension point that has a problem.' };1208message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The message of the problem.' };1209};1210type ExtensionsMessageEvent = {1211type: Severity;1212extensionId: string;1213extensionPointId: string;1214message: string;1215};1216this._telemetryService.publicLog2<ExtensionsMessageEvent, ExtensionsMessageClassification>('extensionsMessage', {1217type, extensionId: extensionId.value, extensionPointId, message1218});1219}1220}12211222private static _handleExtensionPoint<T extends IExtensionContributions[keyof IExtensionContributions]>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {1223const users: IExtensionPointUser<T>[] = [];1224for (const desc of availableExtensions) {1225if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) {1226users.push({1227description: desc,1228value: desc.contributes[extensionPoint.name as keyof typeof desc.contributes] as T,1229collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name)1230});1231}1232}1233extensionPoint.acceptUsers(users);1234}12351236//#region Called by extension host12371238private _acquireInternalAPI(extensionHost: IExtensionHost): IInternalExtensionService {1239return {1240_activateById: (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> => {1241return this._activateById(extensionId, reason);1242},1243_onWillActivateExtension: (extensionId: ExtensionIdentifier): void => {1244return this._onWillActivateExtension(extensionId, extensionHost.runningLocation);1245},1246_onDidActivateExtension: (extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void => {1247return this._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason);1248},1249_onDidActivateExtensionError: (extensionId: ExtensionIdentifier, error: Error): void => {1250return this._onDidActivateExtensionError(extensionId, error);1251},1252_onExtensionRuntimeError: (extensionId: ExtensionIdentifier, err: Error): void => {1253return this._onExtensionRuntimeError(extensionId, err);1254}1255};1256}12571258public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {1259const results = await Promise.all(1260this._extensionHostManagers.map(manager => manager.activate(extensionId, reason))1261);1262const activated = results.some(e => e);1263if (!activated) {1264throw new Error(`Unknown extension ${extensionId.value}`);1265}1266}12671268private _onWillActivateExtension(extensionId: ExtensionIdentifier, runningLocation: ExtensionRunningLocation): void {1269this._runningLocations.set(extensionId, runningLocation);1270const extensionStatus = this._getOrCreateExtensionStatus(extensionId);1271extensionStatus.onWillActivate();1272}12731274private _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void {1275const extensionStatus = this._getOrCreateExtensionStatus(extensionId);1276extensionStatus.setActivationTimes(new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason));1277this._onDidChangeExtensionsStatus.fire([extensionId]);1278}12791280private _onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void {1281type ExtensionActivationErrorClassification = {1282owner: 'alexdima';1283comment: 'An extension failed to activate';1284extensionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the extension.' };1285error: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'The error message.' };1286};1287type ExtensionActivationErrorEvent = {1288extensionId: string;1289error: string;1290};1291this._telemetryService.publicLog2<ExtensionActivationErrorEvent, ExtensionActivationErrorClassification>('extensionActivationError', {1292extensionId: extensionId.value,1293error: error.message1294});1295}12961297private _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void {1298const extensionStatus = this._getOrCreateExtensionStatus(extensionId);1299extensionStatus.addRuntimeError(err);1300this._onDidChangeExtensionsStatus.fire([extensionId]);1301}13021303//#endregion13041305protected abstract _resolveExtensions(): AsyncIterable<ResolvedExtensions>;1306protected abstract _onExtensionHostExit(code: number): Promise<void>;1307protected abstract _resolveAuthority(remoteAuthority: string): Promise<ResolverResult>;1308}13091310class ExtensionHostCollection extends Disposable {13111312private _extensionHostManagers: ExtensionHostManagerData[] = [];13131314public override dispose() {1315for (let i = this._extensionHostManagers.length - 1; i >= 0; i--) {1316const manager = this._extensionHostManagers[i];1317manager.extensionHost.disconnect();1318manager.dispose();1319}1320this._extensionHostManagers = [];1321super.dispose();1322}13231324public add(extensionHostManager: IExtensionHostManager, disposableStore: DisposableStore): void {1325this._extensionHostManagers.push(new ExtensionHostManagerData(extensionHostManager, disposableStore));1326}13271328public async stopAllInReverse(): Promise<void> {1329// See https://github.com/microsoft/vscode/issues/1522041330// Dispose extension hosts in reverse creation order because the local extension host1331// might be critical in sustaining a connection to the remote extension host1332for (let i = this._extensionHostManagers.length - 1; i >= 0; i--) {1333const manager = this._extensionHostManagers[i];1334await manager.extensionHost.disconnect();1335manager.dispose();1336}1337this._extensionHostManagers = [];1338}13391340public async stopOne(extensionHostManager: IExtensionHostManager): Promise<void> {1341const index = this._extensionHostManagers.findIndex(el => el.extensionHost === extensionHostManager);1342if (index >= 0) {1343this._extensionHostManagers.splice(index, 1);1344await extensionHostManager.disconnect();1345extensionHostManager.dispose();1346}1347}13481349public getByKind(kind: ExtensionHostKind): IExtensionHostManager[] {1350return this.filter(el => el.kind === kind);1351}13521353public getByRunningLocation(runningLocation: ExtensionRunningLocation): IExtensionHostManager | null {1354for (const el of this._extensionHostManagers) {1355if (el.extensionHost.representsRunningLocation(runningLocation)) {1356return el.extensionHost;1357}1358}1359return null;1360}13611362*[Symbol.iterator]() {1363for (const extensionHostManager of this._extensionHostManagers) {1364yield extensionHostManager.extensionHost;1365}1366}13671368public map<T>(callback: (extHostManager: IExtensionHostManager) => T): T[] {1369return this._extensionHostManagers.map(el => callback(el.extensionHost));1370}13711372public every(callback: (extHostManager: IExtensionHostManager) => unknown): boolean {1373return this._extensionHostManagers.every(el => callback(el.extensionHost));1374}13751376public filter(callback: (extHostManager: IExtensionHostManager) => unknown): IExtensionHostManager[] {1377return this._extensionHostManagers.filter(el => callback(el.extensionHost)).map(el => el.extensionHost);1378}1379}13801381class ExtensionHostManagerData {1382constructor(1383public readonly extensionHost: IExtensionHostManager,1384public readonly disposableStore: DisposableStore1385) { }13861387public dispose(): void {1388this.disposableStore.dispose();1389this.extensionHost.dispose();1390}1391}13921393export class ResolverExtensions {1394constructor(1395public readonly extensions: IExtensionDescription[],1396) { }1397}13981399export class LocalExtensions {1400constructor(1401public readonly extensions: IExtensionDescription[],1402) { }1403}14041405export class RemoteExtensions {1406constructor(1407public readonly extensions: IExtensionDescription[],1408) { }1409}14101411export type ResolvedExtensions = ResolverExtensions | LocalExtensions | RemoteExtensions;14121413export interface IExtensionHostFactory {1414createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null;1415}14161417class DeltaExtensionsQueueItem {1418constructor(1419public readonly toAdd: IExtension[],1420public readonly toRemove: string[] | IExtension[]1421) { }1422}14231424export function isResolverExtension(extension: IExtensionDescription): boolean {1425return !!extension.activationEvents?.some(activationEvent => activationEvent.startsWith('onResolveRemoteAuthority:'));1426}14271428/**1429* @argument extensions The extensions to be checked.1430* @argument ignoreWorkspaceTrust Do not take workspace trust into account.1431*/1432export function checkEnabledAndProposedAPI(logService: ILogService, extensionEnablementService: IWorkbenchExtensionEnablementService, extensionsProposedApi: ExtensionsProposedApi, extensions: IExtensionDescription[], ignoreWorkspaceTrust: boolean): IExtensionDescription[] {1433// enable or disable proposed API per extension1434extensionsProposedApi.updateEnabledApiProposals(extensions);14351436// keep only enabled extensions1437return filterEnabledExtensions(logService, extensionEnablementService, extensions, ignoreWorkspaceTrust);1438}14391440/**1441* Return the subset of extensions that are enabled.1442* @argument ignoreWorkspaceTrust Do not take workspace trust into account.1443*/1444export function filterEnabledExtensions(logService: ILogService, extensionEnablementService: IWorkbenchExtensionEnablementService, extensions: IExtensionDescription[], ignoreWorkspaceTrust: boolean): IExtensionDescription[] {1445const enabledExtensions: IExtensionDescription[] = [], extensionsToCheck: IExtensionDescription[] = [], mappedExtensions: IExtension[] = [];1446for (const extension of extensions) {1447if (extension.isUnderDevelopment) {1448// Never disable extensions under development1449enabledExtensions.push(extension);1450} else {1451extensionsToCheck.push(extension);1452mappedExtensions.push(toExtension(extension));1453}1454}14551456const enablementStates = extensionEnablementService.getEnablementStates(mappedExtensions, ignoreWorkspaceTrust ? { trusted: true } : undefined);1457for (let index = 0; index < enablementStates.length; index++) {1458if (extensionEnablementService.isEnabledEnablementState(enablementStates[index])) {1459enabledExtensions.push(extensionsToCheck[index]);1460} else {1461if (isCI) {1462logService.info(`filterEnabledExtensions: extension '${extensionsToCheck[index].identifier.value}' is disabled`);1463}1464}1465}14661467return enabledExtensions;1468}14691470/**1471* @argument extension The extension to be checked.1472* @argument ignoreWorkspaceTrust Do not take workspace trust into account.1473*/1474export function extensionIsEnabled(logService: ILogService, extensionEnablementService: IWorkbenchExtensionEnablementService, extension: IExtensionDescription, ignoreWorkspaceTrust: boolean): boolean {1475return filterEnabledExtensions(logService, extensionEnablementService, [extension], ignoreWorkspaceTrust).includes(extension);1476}14771478function includes(extensions: IExtensionDescription[], identifier: ExtensionIdentifier): boolean {1479for (const extension of extensions) {1480if (ExtensionIdentifier.equals(extension.identifier, identifier)) {1481return true;1482}1483}1484return false;1485}14861487export class ExtensionStatus {14881489private readonly _messages: IMessage[] = [];1490public get messages(): IMessage[] {1491return this._messages;1492}14931494private _activationTimes: ActivationTimes | null = null;1495public get activationTimes(): ActivationTimes | null {1496return this._activationTimes;1497}14981499private _runtimeErrors: Error[] = [];1500public get runtimeErrors(): Error[] {1501return this._runtimeErrors;1502}15031504private _activationStarted: boolean = false;1505public get activationStarted(): boolean {1506return this._activationStarted;1507}15081509constructor(1510public readonly id: ExtensionIdentifier,1511) { }15121513public clearRuntimeStatus(): void {1514this._activationStarted = false;1515this._activationTimes = null;1516this._runtimeErrors = [];1517}15181519public addMessage(msg: IMessage): void {1520this._messages.push(msg);1521}15221523public setActivationTimes(activationTimes: ActivationTimes) {1524this._activationTimes = activationTimes;1525}15261527public addRuntimeError(err: Error): void {1528this._runtimeErrors.push(err);1529}15301531public onWillActivate() {1532this._activationStarted = true;1533}1534}15351536interface IExtensionHostCrashInfo {1537timestamp: number;1538}15391540export class ExtensionHostCrashTracker {15411542private static _TIME_LIMIT = 5 * 60 * 1000; // 5 minutes1543private static _CRASH_LIMIT = 3;15441545private readonly _recentCrashes: IExtensionHostCrashInfo[] = [];15461547private _removeOldCrashes(): void {1548const limit = Date.now() - ExtensionHostCrashTracker._TIME_LIMIT;1549while (this._recentCrashes.length > 0 && this._recentCrashes[0].timestamp < limit) {1550this._recentCrashes.shift();1551}1552}15531554public registerCrash(): void {1555this._removeOldCrashes();1556this._recentCrashes.push({ timestamp: Date.now() });1557}15581559public shouldAutomaticallyRestart(): boolean {1560this._removeOldCrashes();1561return (this._recentCrashes.length < ExtensionHostCrashTracker._CRASH_LIMIT);1562}1563}15641565/**1566* This can run correctly only on the renderer process because that is the only place1567* where all extension points and all implicit activation events generators are known.1568*/1569export class ImplicitActivationAwareReader implements IActivationEventsReader {1570public readActivationEvents(extensionDescription: IExtensionDescription): string[] {1571return ImplicitActivationEvents.readActivationEvents(extensionDescription);1572}1573}15741575class ActivationFeatureMarkdowneRenderer extends Disposable implements IExtensionFeatureMarkdownRenderer {15761577readonly type = 'markdown';15781579shouldRender(manifest: IExtensionManifest): boolean {1580return !!manifest.activationEvents;1581}15821583render(manifest: IExtensionManifest): IRenderedData<IMarkdownString> {1584const activationEvents = manifest.activationEvents || [];1585const data = new MarkdownString();1586if (activationEvents.length) {1587for (const activationEvent of activationEvents) {1588data.appendMarkdown(`- \`${activationEvent}\`\n`);1589}1590}1591return {1592data,1593dispose: () => { }1594};1595}1596}15971598Registry.as<IExtensionFeaturesRegistry>(ExtensionFeaturesExtensions.ExtensionFeaturesRegistry).registerExtensionFeature({1599id: 'activationEvents',1600label: nls.localize('activation', "Activation Events"),1601access: {1602canToggle: false1603},1604renderer: new SyncDescriptor(ActivationFeatureMarkdowneRenderer),1605});160616071608