From dc096ff57993d89dc128180687389a56def82e87 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 May 2023 07:34:18 +0200 Subject: [PATCH] extension host - show reason when veto (#180514) --- .../extensions/browser/extensionService.ts | 5 +++- .../common/abstractExtensionService.ts | 26 +++++++++++++++++-- .../services/extensions/common/extensions.ts | 6 ++--- .../nativeExtensionService.ts | 5 +++- .../test/browser/extensionService.test.ts | 4 ++- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index fa581f401e62f..e7f17fdf11c96 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -5,6 +5,7 @@ import { Schemas } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; @@ -63,6 +64,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, @IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService, @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService, + @IDialogService dialogService: IDialogService, ) { const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi); const extensionHostFactory = new BrowserExtensionHostFactory( @@ -93,7 +95,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten remoteAgentService, remoteExtensionsScannerService, lifecycleService, - remoteAuthorityResolverService + remoteAuthorityResolverService, + dialogService ); // Initialize installed extensions first and do it only after workbench is ready diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 37126edc3e915..a19a2d4ed0c5d 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -13,6 +13,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ImplicitActivationEvents } from 'vs/platform/extensionManagement/common/implicitActivationEvents'; import { ExtensionIdentifier, ExtensionIdentifierMap, IExtension, IExtensionContributions, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -104,6 +105,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx @IRemoteExtensionsScannerService protected readonly _remoteExtensionsScannerService: IRemoteExtensionsScannerService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IRemoteAuthorityResolverService protected readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IDialogService private readonly _dialogService: IDialogService, ) { super(); @@ -657,11 +659,24 @@ export abstract class AbstractExtensionService extends Disposable implements IEx private async _doStopExtensionHostsWithVeto(reason: string): Promise { const vetos: (boolean | Promise)[] = []; + const vetoReasons: string[] = []; this._onWillStop.fire({ reason, - veto(value) { + veto(value, reason) { vetos.push(value); + + if (typeof value === 'boolean') { + if (value === true) { + vetoReasons.push(reason); + } + } else { + value.then(value => { + if (value) { + vetoReasons.push(reason); + } + }); + } } }); @@ -669,7 +684,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx if (!veto) { this._doStopExtensionHosts(); } else { - this._logService.warn('Extension Host stop request was vetoed'); + this._logService.warn(`Extension host was not stopped because of veto (stop reason: ${reason}, veto reason: ${vetoReasons.join(', ')})`); + + await this._dialogService.warn( + nls.localize('extensionStopVetoMessage', "The following operation was blocked: {0}", reason), + vetoReasons.length === 1 ? + nls.localize('extensionStopVetoDetailsOne', "The reason for blocking the operation: {0}", vetoReasons[0]) : + nls.localize('extensionStopVetoDetailsMany', "The reasons for blocking the operation:\n- {0}", vetoReasons.join('\n -')), + ); } return !veto; diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index a08bbdc87de9e..521a9d6b93c33 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -336,10 +336,10 @@ export interface WillStopExtensionHostsEvent { * Allows to veto the stopping of extension hosts. The veto can be a long running * operation. * - * @param id to identify the veto operation in case it takes very long or never - * completes. + * @param reason a human readable reason for vetoing the extension host stop in case + * where the resolved `value: true`. */ - veto(value: boolean | Promise, id: string): void; + veto(value: boolean | Promise, reason: string): void; } export interface IExtensionService { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts index 3a5a8e67cf024..202d53035dc39 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts @@ -15,6 +15,7 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -83,6 +84,7 @@ export class NativeExtensionService extends AbstractExtensionService implements @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService, @IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService, @IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService, + @IDialogService dialogService: IDialogService, ) { const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi); const extensionScanner = instantiationService.createInstance(CachedExtensionScanner); @@ -116,7 +118,8 @@ export class NativeExtensionService extends AbstractExtensionService implements remoteAgentService, remoteExtensionsScannerService, lifecycleService, - remoteAuthorityResolverService + remoteAuthorityResolverService, + dialogService ); this._extensionScanner = extensionScanner; diff --git a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts index f7c1e75d5b1b2..6e1f9f07c7bdf 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts @@ -9,6 +9,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { mock } from 'vs/base/test/common/mock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; import { ExtensionKind, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier, IExtension, IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; @@ -176,7 +177,8 @@ suite('ExtensionService', () => { remoteAgentService, remoteExtensionsScannerService, lifecycleService, - remoteAuthorityResolverService + remoteAuthorityResolverService, + new TestDialogService() ); }