Path: blob/main/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.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 { runWhenWindowIdle } from '../../../../base/browser/dom.js';6import { mainWindow } from '../../../../base/browser/window.js';7import { CancellationToken } from '../../../../base/common/cancellation.js';8import { Schemas } from '../../../../base/common/network.js';9import * as performance from '../../../../base/common/performance.js';10import { isCI } from '../../../../base/common/platform.js';11import { URI } from '../../../../base/common/uri.js';12import * as nls from '../../../../nls.js';13import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';14import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';15import { ICommandService } from '../../../../platform/commands/common/commands.js';16import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';17import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';18import { ExtensionKind } from '../../../../platform/environment/common/environment.js';19import { IExtensionGalleryService } from '../../../../platform/extensionManagement/common/extensionManagement.js';20import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';21import { IFileService } from '../../../../platform/files/common/files.js';22import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';23import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';24import { ILogService } from '../../../../platform/log/common/log.js';25import { INativeHostService } from '../../../../platform/native/common/native.js';26import { INotificationService, IPromptChoice, NotificationPriority, Severity } from '../../../../platform/notification/common/notification.js';27import { IOpenerService } from '../../../../platform/opener/common/opener.js';28import { IProductService } from '../../../../platform/product/common/productService.js';29import { PersistentConnectionEventType } from '../../../../platform/remote/common/remoteAgentConnection.js';30import { IRemoteAgentEnvironment } from '../../../../platform/remote/common/remoteAgentEnvironment.js';31import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteConnectionType, ResolverResult, getRemoteAuthorityPrefix } from '../../../../platform/remote/common/remoteAuthorityResolver.js';32import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js';33import { getRemoteName, parseAuthorityWithPort } from '../../../../platform/remote/common/remoteHosts.js';34import { updateProxyConfigurationsScope } from '../../../../platform/request/common/request.js';35import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';36import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';37import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';38import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';39import { EnablementState, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../extensionManagement/common/extensionManagement.js';40import { IWebWorkerExtensionHostDataProvider, IWebWorkerExtensionHostInitData, WebWorkerExtensionHost } from '../browser/webWorkerExtensionHost.js';41import { AbstractExtensionService, ExtensionHostCrashTracker, IExtensionHostFactory, LocalExtensions, RemoteExtensions, ResolvedExtensions, ResolverExtensions, checkEnabledAndProposedAPI, extensionIsEnabled, isResolverExtension } from '../common/abstractExtensionService.js';42import { ExtensionDescriptionRegistrySnapshot } from '../common/extensionDescriptionRegistry.js';43import { parseExtensionDevOptions } from '../common/extensionDevOptions.js';44import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, extensionHostKindToString, extensionRunningPreferenceToString } from '../common/extensionHostKind.js';45import { IExtensionHostManager } from '../common/extensionHostManagers.js';46import { ExtensionHostExitCode } from '../common/extensionHostProtocol.js';47import { IExtensionManifestPropertiesService } from '../common/extensionManifestPropertiesService.js';48import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation } from '../common/extensionRunningLocation.js';49import { ExtensionRunningLocationTracker, filterExtensionDescriptions } from '../common/extensionRunningLocationTracker.js';50import { ExtensionHostExtensions, ExtensionHostStartup, IExtensionHost, IExtensionService, WebWorkerExtHostConfigValue, toExtension, webWorkerExtHostConfig } from '../common/extensions.js';51import { ExtensionsProposedApi } from '../common/extensionsProposedApi.js';52import { IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData, RemoteExtensionHost } from '../common/remoteExtensionHost.js';53import { CachedExtensionScanner } from './cachedExtensionScanner.js';54import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData, NativeLocalProcessExtensionHost } from './localProcessExtensionHost.js';55import { IHostService } from '../../host/browser/host.js';56import { ILifecycleService, LifecyclePhase } from '../../lifecycle/common/lifecycle.js';57import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';58import { IRemoteExplorerService } from '../../remote/common/remoteExplorerService.js';59import { AsyncIterableEmitter, AsyncIterableObject } from '../../../../base/common/async.js';6061export class NativeExtensionService extends AbstractExtensionService implements IExtensionService {6263private readonly _extensionScanner: CachedExtensionScanner;64private readonly _localCrashTracker = new ExtensionHostCrashTracker();6566constructor(67@IInstantiationService instantiationService: IInstantiationService,68@INotificationService notificationService: INotificationService,69@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,70@ITelemetryService telemetryService: ITelemetryService,71@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService,72@IFileService fileService: IFileService,73@IProductService productService: IProductService,74@IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService,75@IWorkspaceContextService contextService: IWorkspaceContextService,76@IConfigurationService configurationService: IConfigurationService,77@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,78@ILogService logService: ILogService,79@IRemoteAgentService remoteAgentService: IRemoteAgentService,80@IRemoteExtensionsScannerService remoteExtensionsScannerService: IRemoteExtensionsScannerService,81@ILifecycleService lifecycleService: ILifecycleService,82@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService,83@INativeHostService private readonly _nativeHostService: INativeHostService,84@IHostService private readonly _hostService: IHostService,85@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,86@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,87@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,88@IDialogService dialogService: IDialogService,89) {90const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi);91const extensionScanner = instantiationService.createInstance(CachedExtensionScanner);92const extensionHostFactory = new NativeExtensionHostFactory(93extensionsProposedApi,94extensionScanner,95() => this._getExtensionRegistrySnapshotWhenReady(),96instantiationService,97environmentService,98extensionEnablementService,99configurationService,100remoteAgentService,101remoteAuthorityResolverService,102logService103);104super(105{ hasLocalProcess: true, allowRemoteExtensionsInLocalWebWorker: false },106extensionsProposedApi,107extensionHostFactory,108new NativeExtensionHostKindPicker(environmentService, configurationService, logService),109instantiationService,110notificationService,111environmentService,112telemetryService,113extensionEnablementService,114fileService,115productService,116extensionManagementService,117contextService,118configurationService,119extensionManifestPropertiesService,120logService,121remoteAgentService,122remoteExtensionsScannerService,123lifecycleService,124remoteAuthorityResolverService,125dialogService126);127128this._extensionScanner = extensionScanner;129130// delay extension host creation and extension scanning131// until the workbench is running. we cannot defer the132// extension host more (LifecyclePhase.Restored) because133// some editors require the extension host to restore134// and this would result in a deadlock135// see https://github.com/microsoft/vscode/issues/41322136lifecycleService.when(LifecyclePhase.Ready).then(() => {137// reschedule to ensure this runs after restoring viewlets, panels, and editors138runWhenWindowIdle(mainWindow, () => {139this._initialize();140}, 50 /*max delay*/);141});142}143144private async _scanAllLocalExtensions(): Promise<IExtensionDescription[]> {145return this._extensionScanner.scannedExtensions;146}147148protected override _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {149150const activatedExtensions: ExtensionIdentifier[] = [];151const extensionsStatus = this.getExtensionsStatus();152for (const key of Object.keys(extensionsStatus)) {153const extensionStatus = extensionsStatus[key];154if (extensionStatus.activationStarted && extensionHost.containsExtension(extensionStatus.id)) {155activatedExtensions.push(extensionStatus.id);156}157}158159super._onExtensionHostCrashed(extensionHost, code, signal);160161if (extensionHost.kind === ExtensionHostKind.LocalProcess) {162if (code === ExtensionHostExitCode.VersionMismatch) {163this._notificationService.prompt(164Severity.Error,165nls.localize('extensionService.versionMismatchCrash', "Extension host cannot start: version mismatch."),166[{167label: nls.localize('relaunch', "Relaunch VS Code"),168run: () => {169this._instantiationService.invokeFunction((accessor) => {170const hostService = accessor.get(IHostService);171hostService.restart();172});173}174}]175);176return;177}178179this._logExtensionHostCrash(extensionHost);180this._sendExtensionHostCrashTelemetry(code, signal, activatedExtensions);181182this._localCrashTracker.registerCrash();183184if (this._localCrashTracker.shouldAutomaticallyRestart()) {185this._logService.info(`Automatically restarting the extension host.`);186this._notificationService.status(nls.localize('extensionService.autoRestart', "The extension host terminated unexpectedly. Restarting..."), { hideAfter: 5000 });187this.startExtensionHosts();188} else {189const choices: IPromptChoice[] = [];190if (this._environmentService.isBuilt) {191choices.push({192label: nls.localize('startBisect', "Start Extension Bisect"),193run: () => {194this._instantiationService.invokeFunction(accessor => {195const commandService = accessor.get(ICommandService);196commandService.executeCommand('extension.bisect.start');197});198}199});200} else {201choices.push({202label: nls.localize('devTools', "Open Developer Tools"),203run: () => this._nativeHostService.openDevTools()204});205}206207choices.push({208label: nls.localize('restart', "Restart Extension Host"),209run: () => this.startExtensionHosts()210});211212if (this._environmentService.isBuilt) {213choices.push({214label: nls.localize('learnMore', "Learn More"),215run: () => {216this._instantiationService.invokeFunction(accessor => {217const openerService = accessor.get(IOpenerService);218openerService.open('https://aka.ms/vscode-extension-bisect');219});220}221});222}223224this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly 3 times within the last 5 minutes."), choices);225}226}227}228229private _sendExtensionHostCrashTelemetry(code: number, signal: string | null, activatedExtensions: ExtensionIdentifier[]): void {230type ExtensionHostCrashClassification = {231owner: 'alexdima';232comment: 'The extension host has terminated unexpectedly';233code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The exit code of the extension host process.' };234signal: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The signal that caused the extension host process to exit.' };235extensionIds: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The list of loaded extensions.' };236};237type ExtensionHostCrashEvent = {238code: number;239signal: string | null;240extensionIds: string[];241};242this._telemetryService.publicLog2<ExtensionHostCrashEvent, ExtensionHostCrashClassification>('extensionHostCrash', {243code,244signal,245extensionIds: activatedExtensions.map(e => e.value)246});247248for (const extensionId of activatedExtensions) {249type ExtensionHostCrashExtensionClassification = {250owner: 'alexdima';251comment: 'The extension host has terminated unexpectedly';252code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The exit code of the extension host process.' };253signal: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The signal that caused the extension host process to exit.' };254extensionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the extension.' };255};256type ExtensionHostCrashExtensionEvent = {257code: number;258signal: string | null;259extensionId: string;260};261this._telemetryService.publicLog2<ExtensionHostCrashExtensionEvent, ExtensionHostCrashExtensionClassification>('extensionHostCrashExtension', {262code,263signal,264extensionId: extensionId.value265});266}267}268269// --- impl270271protected async _resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {272273const authorityPlusIndex = remoteAuthority.indexOf('+');274if (authorityPlusIndex === -1) {275// This authority does not need to be resolved, simply parse the port number276const { host, port } = parseAuthorityWithPort(remoteAuthority);277return {278authority: {279authority: remoteAuthority,280connectTo: {281type: RemoteConnectionType.WebSocket,282host,283port284},285connectionToken: undefined286}287};288}289290return this._resolveAuthorityOnExtensionHosts(ExtensionHostKind.LocalProcess, remoteAuthority);291}292293private async _getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {294295const authorityPlusIndex = remoteAuthority.indexOf('+');296if (authorityPlusIndex === -1) {297// This authority does not use a resolver298return uri;299}300301const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);302if (localProcessExtensionHosts.length === 0) {303// no local process extension hosts304throw new Error(`Cannot resolve canonical URI`);305}306307const results = await Promise.all(localProcessExtensionHosts.map(extHost => extHost.getCanonicalURI(remoteAuthority, uri)));308309for (const result of results) {310if (result) {311return result;312}313}314315// we can only reach this if there was no resolver extension that can return the cannonical uri316throw new Error(`Cannot get canonical URI because no extension is installed to resolve ${getRemoteAuthorityPrefix(remoteAuthority)}`);317}318319protected _resolveExtensions(): AsyncIterable<ResolvedExtensions> {320return new AsyncIterableObject(emitter => this._doResolveExtensions(emitter));321}322323private async _doResolveExtensions(emitter: AsyncIterableEmitter<ResolvedExtensions>): Promise<void> {324this._extensionScanner.startScanningExtensions();325326const remoteAuthority = this._environmentService.remoteAuthority;327328let remoteEnv: IRemoteAgentEnvironment | null = null;329let remoteExtensions: IExtensionDescription[] = [];330331if (remoteAuthority) {332333this._remoteAuthorityResolverService._setCanonicalURIProvider(async (uri) => {334if (uri.scheme !== Schemas.vscodeRemote || uri.authority !== remoteAuthority) {335// The current remote authority resolver cannot give the canonical URI for this URI336return uri;337}338performance.mark(`code/willGetCanonicalURI/${getRemoteAuthorityPrefix(remoteAuthority)}`);339if (isCI) {340this._logService.info(`Invoking getCanonicalURI for authority ${getRemoteAuthorityPrefix(remoteAuthority)}...`);341}342try {343return this._getCanonicalURI(remoteAuthority, uri);344} finally {345performance.mark(`code/didGetCanonicalURI/${getRemoteAuthorityPrefix(remoteAuthority)}`);346if (isCI) {347this._logService.info(`getCanonicalURI returned for authority ${getRemoteAuthorityPrefix(remoteAuthority)}.`);348}349}350});351352if (isCI) {353this._logService.info(`Starting to wait on IWorkspaceTrustManagementService.workspaceResolved...`);354}355356// Now that the canonical URI provider has been registered, we need to wait for the trust state to be357// calculated. The trust state will be used while resolving the authority, however the resolver can358// override the trust state through the resolver result.359await this._workspaceTrustManagementService.workspaceResolved;360361if (isCI) {362this._logService.info(`Finished waiting on IWorkspaceTrustManagementService.workspaceResolved.`);363}364365const localExtensions = await this._scanAllLocalExtensions();366const resolverExtensions = localExtensions.filter(extension => isResolverExtension(extension));367if (resolverExtensions.length) {368emitter.emitOne(new ResolverExtensions(resolverExtensions));369}370371let resolverResult: ResolverResult;372try {373resolverResult = await this._resolveAuthorityInitial(remoteAuthority);374} catch (err) {375if (RemoteAuthorityResolverError.isNoResolverFound(err)) {376err.isHandled = await this._handleNoResolverFound(remoteAuthority);377} else {378if (RemoteAuthorityResolverError.isHandled(err)) {379console.log(`Error handled: Not showing a notification for the error`);380}381}382this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);383384// Proceed with the local extension host385return this._startLocalExtensionHost(emitter);386}387388// set the resolved authority389this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options);390this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation);391392// monitor for breakage393const connection = this._remoteAgentService.getConnection();394if (connection) {395this._register(connection.onDidStateChange(async (e) => {396if (e.type === PersistentConnectionEventType.ConnectionLost) {397this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);398}399}));400this._register(connection.onReconnecting(() => this._resolveAuthorityAgain()));401}402403// fetch the remote environment404[remoteEnv, remoteExtensions] = await Promise.all([405this._remoteAgentService.getEnvironment(),406this._remoteExtensionsScannerService.scanExtensions()407]);408409if (!remoteEnv) {410this._notificationService.notify({ severity: Severity.Error, message: nls.localize('getEnvironmentFailure', "Could not fetch remote environment") });411// Proceed with the local extension host412return this._startLocalExtensionHost(emitter);413}414415const useHostProxyDefault = remoteEnv.useHostProxy;416this._register(this._configurationService.onDidChangeConfiguration(e => {417if (e.affectsConfiguration('http.useLocalProxyConfiguration')) {418updateProxyConfigurationsScope(this._configurationService.getValue('http.useLocalProxyConfiguration'), useHostProxyDefault);419}420}));421updateProxyConfigurationsScope(this._configurationService.getValue('http.useLocalProxyConfiguration'), useHostProxyDefault);422} else {423424this._remoteAuthorityResolverService._setCanonicalURIProvider(async (uri) => uri);425426}427428return this._startLocalExtensionHost(emitter, remoteExtensions);429}430431private async _startLocalExtensionHost(emitter: AsyncIterableEmitter<ResolvedExtensions>, remoteExtensions: IExtensionDescription[] = []): Promise<void> {432// Ensure that the workspace trust state has been fully initialized so433// that the extension host can start with the correct set of extensions.434await this._workspaceTrustManagementService.workspaceTrustInitialized;435436if (remoteExtensions.length) {437emitter.emitOne(new RemoteExtensions(remoteExtensions));438}439440emitter.emitOne(new LocalExtensions(await this._scanAllLocalExtensions()));441}442443protected async _onExtensionHostExit(code: number): Promise<void> {444// Dispose everything associated with the extension host445await this._doStopExtensionHosts();446447// Dispose the management connection to avoid reconnecting after the extension host exits448const connection = this._remoteAgentService.getConnection();449connection?.dispose();450451if (parseExtensionDevOptions(this._environmentService).isExtensionDevTestFromCli) {452// When CLI testing make sure to exit with proper exit code453if (isCI) {454this._logService.info(`Asking native host service to exit with code ${code}.`);455}456this._nativeHostService.exit(code);457} else {458// Expected development extension termination: When the extension host goes down we also shutdown the window459this._nativeHostService.closeWindow();460}461}462463private async _handleNoResolverFound(remoteAuthority: string): Promise<boolean> {464const remoteName = getRemoteName(remoteAuthority);465const recommendation = this._productService.remoteExtensionTips?.[remoteName];466if (!recommendation) {467return false;468}469470const resolverExtensionId = recommendation.extensionId;471const allExtensions = await this._scanAllLocalExtensions();472const extension = allExtensions.filter(e => e.identifier.value === resolverExtensionId)[0];473if (extension) {474if (!extensionIsEnabled(this._logService, this._extensionEnablementService, extension, false)) {475const message = nls.localize('enableResolver', "Extension '{0}' is required to open the remote window.\nOK to enable?", recommendation.friendlyName);476this._notificationService.prompt(Severity.Info, message,477[{478label: nls.localize('enable', 'Enable and Reload'),479run: async () => {480await this._extensionEnablementService.setEnablement([toExtension(extension)], EnablementState.EnabledGlobally);481await this._hostService.reload();482}483}],484{485sticky: true,486priority: NotificationPriority.URGENT487}488);489}490} else {491// Install the Extension and reload the window to handle.492const message = nls.localize('installResolver', "Extension '{0}' is required to open the remote window.\nDo you want to install the extension?", recommendation.friendlyName);493this._notificationService.prompt(Severity.Info, message,494[{495label: nls.localize('install', 'Install and Reload'),496run: async () => {497const [galleryExtension] = await this._extensionGalleryService.getExtensions([{ id: resolverExtensionId }], CancellationToken.None);498if (galleryExtension) {499await this._extensionManagementService.installFromGallery(galleryExtension);500await this._hostService.reload();501} else {502this._notificationService.error(nls.localize('resolverExtensionNotFound', "`{0}` not found on marketplace"));503}504505}506}],507{508sticky: true,509priority: NotificationPriority.URGENT,510}511);512513}514return true;515}516}517518class NativeExtensionHostFactory implements IExtensionHostFactory {519520private readonly _webWorkerExtHostEnablement: LocalWebWorkerExtHostEnablement;521522constructor(523private readonly _extensionsProposedApi: ExtensionsProposedApi,524private readonly _extensionScanner: CachedExtensionScanner,525private readonly _getExtensionRegistrySnapshotWhenReady: () => Promise<ExtensionDescriptionRegistrySnapshot>,526@IInstantiationService private readonly _instantiationService: IInstantiationService,527@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,528@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,529@IConfigurationService configurationService: IConfigurationService,530@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,531@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,532@ILogService private readonly _logService: ILogService,533) {534this._webWorkerExtHostEnablement = determineLocalWebWorkerExtHostEnablement(environmentService, configurationService);535}536537public createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {538switch (runningLocation.kind) {539case ExtensionHostKind.LocalProcess: {540const startup = (541isInitialStart542? ExtensionHostStartup.EagerManualStart543: ExtensionHostStartup.EagerAutoStart544);545return this._instantiationService.createInstance(NativeLocalProcessExtensionHost, runningLocation, startup, this._createLocalProcessExtensionHostDataProvider(runningLocations, isInitialStart, runningLocation));546}547case ExtensionHostKind.LocalWebWorker: {548if (this._webWorkerExtHostEnablement !== LocalWebWorkerExtHostEnablement.Disabled) {549const startup = this._webWorkerExtHostEnablement === LocalWebWorkerExtHostEnablement.Lazy ? ExtensionHostStartup.LazyAutoStart : ExtensionHostStartup.EagerManualStart;550return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, startup, this._createWebWorkerExtensionHostDataProvider(runningLocations, runningLocation));551}552return null;553}554case ExtensionHostKind.Remote: {555const remoteAgentConnection = this._remoteAgentService.getConnection();556if (remoteAgentConnection) {557return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(runningLocations, remoteAgentConnection.remoteAuthority));558}559return null;560}561}562}563564private _createLocalProcessExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, isInitialStart: boolean, desiredRunningLocation: LocalProcessRunningLocation): ILocalProcessExtensionHostDataProvider {565return {566getInitData: async (): Promise<ILocalProcessExtensionHostInitData> => {567if (isInitialStart) {568// Here we load even extensions that would be disabled by workspace trust569const scannedExtensions = await this._extensionScanner.scannedExtensions;570if (isCI) {571this._logService.info(`NativeExtensionHostFactory._createLocalProcessExtensionHostDataProvider.scannedExtensions: ${scannedExtensions.map(ext => ext.identifier.value).join(',')}`);572}573574const localExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, scannedExtensions, /* ignore workspace trust */true);575if (isCI) {576this._logService.info(`NativeExtensionHostFactory._createLocalProcessExtensionHostDataProvider.localExtensions: ${localExtensions.map(ext => ext.identifier.value).join(',')}`);577}578579const runningLocation = runningLocations.computeRunningLocation(localExtensions, [], false);580const myExtensions = filterExtensionDescriptions(localExtensions, runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));581const extensions = new ExtensionHostExtensions(0, localExtensions, myExtensions.map(extension => extension.identifier));582if (isCI) {583this._logService.info(`NativeExtensionHostFactory._createLocalProcessExtensionHostDataProvider.myExtensions: ${myExtensions.map(ext => ext.identifier.value).join(',')}`);584}585return { extensions };586} else {587// restart case588const snapshot = await this._getExtensionRegistrySnapshotWhenReady();589const myExtensions = runningLocations.filterByRunningLocation(snapshot.extensions, desiredRunningLocation);590const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));591return { extensions };592}593}594};595}596597private _createWebWorkerExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, desiredRunningLocation: LocalWebWorkerRunningLocation): IWebWorkerExtensionHostDataProvider {598return {599getInitData: async (): Promise<IWebWorkerExtensionHostInitData> => {600const snapshot = await this._getExtensionRegistrySnapshotWhenReady();601const myExtensions = runningLocations.filterByRunningLocation(snapshot.extensions, desiredRunningLocation);602const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));603return { extensions };604}605};606}607608private _createRemoteExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, remoteAuthority: string): IRemoteExtensionHostDataProvider {609return {610remoteAuthority: remoteAuthority,611getInitData: async (): Promise<IRemoteExtensionHostInitData> => {612const snapshot = await this._getExtensionRegistrySnapshotWhenReady();613614const remoteEnv = await this._remoteAgentService.getEnvironment();615if (!remoteEnv) {616throw new Error('Cannot provide init data for remote extension host!');617}618619const myExtensions = runningLocations.filterByExtensionHostKind(snapshot.extensions, ExtensionHostKind.Remote);620const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));621622return {623connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAuthority),624pid: remoteEnv.pid,625appRoot: remoteEnv.appRoot,626extensionHostLogsPath: remoteEnv.extensionHostLogsPath,627globalStorageHome: remoteEnv.globalStorageHome,628workspaceStorageHome: remoteEnv.workspaceStorageHome,629extensions,630};631}632};633}634}635636function determineLocalWebWorkerExtHostEnablement(environmentService: IWorkbenchEnvironmentService, configurationService: IConfigurationService): LocalWebWorkerExtHostEnablement {637if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentKind?.some(k => k === 'web')) {638return LocalWebWorkerExtHostEnablement.Eager;639} else {640const config = configurationService.getValue<WebWorkerExtHostConfigValue>(webWorkerExtHostConfig);641if (config === true) {642return LocalWebWorkerExtHostEnablement.Eager;643} else if (config === 'auto') {644return LocalWebWorkerExtHostEnablement.Lazy;645} else {646return LocalWebWorkerExtHostEnablement.Disabled;647}648}649}650651const enum LocalWebWorkerExtHostEnablement {652Disabled = 0,653Eager = 1,654Lazy = 2655}656657export class NativeExtensionHostKindPicker implements IExtensionHostKindPicker {658659private readonly _hasRemoteExtHost: boolean;660private readonly _hasWebWorkerExtHost: boolean;661662constructor(663@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,664@IConfigurationService configurationService: IConfigurationService,665@ILogService private readonly _logService: ILogService,666) {667this._hasRemoteExtHost = Boolean(environmentService.remoteAuthority);668const webWorkerExtHostEnablement = determineLocalWebWorkerExtHostEnablement(environmentService, configurationService);669this._hasWebWorkerExtHost = (webWorkerExtHostEnablement !== LocalWebWorkerExtHostEnablement.Disabled);670}671672public pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {673const result = NativeExtensionHostKindPicker.pickExtensionHostKind(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, this._hasRemoteExtHost, this._hasWebWorkerExtHost);674this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);675return result;676}677678public static pickExtensionHostKind(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionHostKind | null {679const result: ExtensionHostKind[] = [];680for (const extensionKind of extensionKinds) {681if (extensionKind === 'ui' && isInstalledLocally) {682// ui extensions run locally if possible683if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {684return ExtensionHostKind.LocalProcess;685} else {686result.push(ExtensionHostKind.LocalProcess);687}688}689if (extensionKind === 'workspace' && isInstalledRemotely) {690// workspace extensions run remotely if possible691if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {692return ExtensionHostKind.Remote;693} else {694result.push(ExtensionHostKind.Remote);695}696}697if (extensionKind === 'workspace' && !hasRemoteExtHost) {698// workspace extensions also run locally if there is no remote699if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {700return ExtensionHostKind.LocalProcess;701} else {702result.push(ExtensionHostKind.LocalProcess);703}704}705if (extensionKind === 'web' && isInstalledLocally && hasWebWorkerExtHost) {706// web worker extensions run in the local web worker if possible707if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {708return ExtensionHostKind.LocalWebWorker;709} else {710result.push(ExtensionHostKind.LocalWebWorker);711}712}713}714return (result.length > 0 ? result[0] : null);715}716}717718class RestartExtensionHostAction extends Action2 {719720constructor() {721super({722id: 'workbench.action.restartExtensionHost',723title: nls.localize2('restartExtensionHost', "Restart Extension Host"),724category: Categories.Developer,725f1: true726});727}728729async run(accessor: ServicesAccessor): Promise<void> {730const extensionService = accessor.get(IExtensionService);731732const stopped = await extensionService.stopExtensionHosts(nls.localize('restartExtensionHost.reason', "An explicit request"));733if (stopped) {734extensionService.startExtensionHosts();735}736}737}738739registerAction2(RestartExtensionHostAction);740741registerSingleton(IExtensionService, NativeExtensionService, InstantiationType.Eager);742743744