diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index 393745571d1..b59e0c63aff 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -5,7 +5,7 @@ import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IReportedExtension, IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement'; import { compareIgnoreCase } from 'vs/base/common/strings'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension } from 'vs/platform/extensions/common/extensions'; export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean { if (a.uuid && b.uuid) { @@ -128,3 +128,22 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set, extension: IExtension): IExtension[] { + const dependencies: IExtension[] = []; + const extensions = extension.manifest.extensionDependencies?.slice(0) ?? []; + + while (extensions.length) { + const id = extensions.shift(); + + if (id && dependencies.every(e => !areSameExtensions(e.identifier, { id }))) { + const ext = installedExtensions.filter(e => areSameExtensions(e.identifier, { id })); + if (ext.length === 1) { + dependencies.push(ext[0]); + extensions.push(...ext[0].manifest.extensionDependencies?.slice(0) ?? []); + } + } + } + + return dependencies; +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 1375e63f672..97cb5675155 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2167,10 +2167,10 @@ export class SystemDisabledWarningAction extends ExtensionAction { return; } } - - const untrustedSupportType = this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(this.extension.local.manifest); - if (this.workspaceTrustService.workspaceTrustEnabled && untrustedSupportType !== true && !this.workspaceTrustService.isWorkpaceTrusted()) { + if (this.workspaceTrustService.workspaceTrustEnabled && !this.workspaceTrustService.isWorkpaceTrusted() && this.extension.enablementState === EnablementState.DisabledByTrustRequirement) { + const untrustedSupportType = this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(this.extension.local.manifest); const untrustedDetails = getWorkpaceSupportTypeMessage(this.extension.local.manifest.capabilities?.untrustedWorkspaces); + this.enabled = true; this.class = `${SystemDisabledWarningAction.TRUST_CLASS}`; this.tooltip = untrustedDetails || (untrustedSupportType === 'limited' ? diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 7ee9c035826..8538ca17100 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -11,7 +11,7 @@ import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/comm import { SortBy, SortOrder, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService, EnablementState, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions, getExtensionDependencies } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { append, $ } from 'vs/base/browser/dom'; @@ -577,7 +577,22 @@ export class ExtensionsListView extends ViewPane { } const hasVirtualSupportType = (extension: IExtension, supportType: ExtensionVirtualWorkpaceSupportType) => extension.local && this.extensionManifestPropertiesService.getExtensionVirtualWorkspaceSupportType(extension.local.manifest) === supportType; - const hasRestrictedSupportType = (extension: IExtension, supportType: ExtensionUntrustedWorkpaceSupportType) => extension.local && this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) === supportType; + const hasRestrictedSupportType = (extension: IExtension, supportType: ExtensionUntrustedWorkpaceSupportType) => { + if (!extension.local) { + return false; + } + + if (this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.local.manifest) === supportType) { + return true; + } + + if (supportType === false) { + const dependencies = getExtensionDependencies(local.map(ext => ext.local!), extension.local); + return dependencies.some(ext => this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(ext.manifest) === supportType); + } + + return false; + }; if (type === 'virtual') { // show limited and disabled extensions unless disabled because of a untrusted workspace diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index 6c5fd0a7c1d..4b591b23973 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -25,7 +25,6 @@ import { localize } from 'vs/nls'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { ExtensionUntrustedWorkpaceSupportType } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchTable } from 'vs/platform/list/browser/listService'; @@ -52,6 +51,7 @@ import { IExtensionManifestPropertiesService } from 'vs/workbench/services/exten import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; import { WorkspaceTrustEditorInput } from 'vs/workbench/services/workspaces/browser/workspaceTrustEditorInput'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { getExtensionDependencies } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; export const shieldIcon = registerCodicon('workspace-trust-icon', Codicon.shield); @@ -765,9 +765,7 @@ export class WorkspaceTrustEditor extends EditorPane { }).length; // Features List - const onDemandExtensionCount = this.getExtensionCountByUntrustedWorkspaceSupport('limited'); - const onStartExtensionCount = this.getExtensionCountByUntrustedWorkspaceSupport(false); - this.renderAffectedFeatures(settingsRequiringTrustedWorkspaceCount, onDemandExtensionCount + onStartExtensionCount); + this.renderAffectedFeatures(settingsRequiringTrustedWorkspaceCount, this.getExtensionCount()); // Configuration Tree this.workpaceTrustedUrisTable.updateTable(); @@ -778,14 +776,25 @@ export class WorkspaceTrustEditor extends EditorPane { this.rendering = false; } - private getExtensionCountByUntrustedWorkspaceSupport(trustRequestType: ExtensionUntrustedWorkpaceSupportType): number { + private getExtensionCount(): number { const set = new Set(); + const inVirtualWorkspace = isVirtualWorkspace(this.workspaceService.getWorkspace()); const localExtensions = this.extensionWorkbenchService.local.filter(ext => ext.local).map(ext => ext.local!); - const filtered = localExtensions.filter(ext => this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(ext.manifest) === trustRequestType); - for (const ext of filtered) { - if (!inVirtualWorkspace || this.extensionManifestPropertiesService.getExtensionVirtualWorkspaceSupportType(ext.manifest) !== false) { - set.add(ext.identifier.id); + + for (const extension of localExtensions) { + if (inVirtualWorkspace && this.extensionManifestPropertiesService.getExtensionVirtualWorkspaceSupportType(extension.manifest) === false) { + continue; + } + + if (this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.manifest) !== true) { + set.add(extension.identifier.id); + continue; + } + + const dependencies = getExtensionDependencies(localExtensions, extension); + if (dependencies.some(ext => this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(ext.manifest) === false)) { + set.add(extension.identifier.id); } } diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 7e64c80b81e..df282fc698c 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, IExtensionIdentifier, IGlobalExtensionEnablementService, ENABLED_EXTENSIONS_STORAGE_PATH, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IWorkbenchExtensionManagementService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { areSameExtensions, BetterMergeId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions, BetterMergeId, getExtensionDependencies } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -245,10 +245,11 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench else { enablementState = this._getUserEnablementState(extension.identifier); if (this._isEnabledEnablementState(enablementState)) { - if (this._isDisabledByWorkspaceTrust(extension)) { + if (this._isDisabledByWorkspaceTrust(extension, extensions)) { enablementState = EnablementState.DisabledByTrustRequirement; } - if (this._isDisabledByExtensionDependency(extension, extensions, computedEnablementStates)) { + + else if (this._isDisabledByExtensionDependency(extension, extensions, computedEnablementStates)) { enablementState = EnablementState.DisabledByExtensionDependency; } } @@ -316,12 +317,14 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench return false; } - private _isDisabledByWorkspaceTrust(extension: IExtension): boolean { + private _isDisabledByWorkspaceTrust(extension: IExtension, extensions: ReadonlyArray): boolean { if (this.workspaceTrustManagementService.isWorkpaceTrusted()) { return false; } - return this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.manifest) === false; + // Find dependencies from the same server as of the extension + const installedExtensions = extensions.filter(e => this.extensionManagementServerService.getExtensionManagementServer(e) === this.extensionManagementServerService.getExtensionManagementServer(extension)); + return [extension, ...getExtensionDependencies(installedExtensions, extension)].some(extension => this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.manifest) === false); } private _isDisabledByExtensionDependency(extension: IExtension, extensions: ReadonlyArray, computedEnablementStates: Map): boolean { @@ -501,10 +504,15 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench public async updateEnablementByWorkspaceTrustRequirement(): Promise { await this.extensionsManager.whenInitialized(); + const disabledExtensions = this.extensionsManager.extensions - .filter(extension => - this._isEnabledEnablementState(this._getUserEnablementState(extension.identifier)) && - this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.manifest) === false); + .filter(extension => { + const dependencies = getExtensionDependencies(this.extensionsManager.extensions, extension); + const isEnabled = this._isEnabledEnablementState(this._getUserEnablementState(extension.identifier)); + + return isEnabled && (this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(extension.manifest) === false || + dependencies.some(ext => this.extensionManifestPropertiesService.getExtensionUntrustedWorkspaceSupportType(ext.manifest) === false)); + }); if (disabledExtensions.length) { this._onEnablementChanged.fire(disabledExtensions);