Path: blob/main/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.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 { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';6import { IExtensionManifest, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionIdentifier, ALL_EXTENSION_KINDS, ExtensionIdentifierMap } from '../../../../platform/extensions/common/extensions.js';7import { ExtensionKind } from '../../../../platform/environment/common/environment.js';8import { ExtensionsRegistry } from './extensionsRegistry.js';9import { getGalleryExtensionId } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';10import { isNonEmptyArray } from '../../../../base/common/arrays.js';11import { IProductService } from '../../../../platform/product/common/productService.js';12import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';13import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';14import { ExtensionUntrustedWorkspaceSupport } from '../../../../base/common/product.js';15import { Disposable } from '../../../../base/common/lifecycle.js';16import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from '../../workspaces/common/workspaceTrust.js';17import { isBoolean } from '../../../../base/common/types.js';18import { IWorkspaceTrustEnablementService } from '../../../../platform/workspace/common/workspaceTrust.js';19import { ILogService } from '../../../../platform/log/common/log.js';20import { isWeb } from '../../../../base/common/platform.js';2122export const IExtensionManifestPropertiesService = createDecorator<IExtensionManifestPropertiesService>('extensionManifestPropertiesService');2324export interface IExtensionManifestPropertiesService {25readonly _serviceBrand: undefined;2627prefersExecuteOnUI(manifest: IExtensionManifest): boolean;28prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean;29prefersExecuteOnWeb(manifest: IExtensionManifest): boolean;3031canExecuteOnUI(manifest: IExtensionManifest): boolean;32canExecuteOnWorkspace(manifest: IExtensionManifest): boolean;33canExecuteOnWeb(manifest: IExtensionManifest): boolean;3435getExtensionKind(manifest: IExtensionManifest): ExtensionKind[];36getUserConfiguredExtensionKind(extensionIdentifier: IExtensionIdentifier): ExtensionKind[] | undefined;37getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupportType;38getExtensionVirtualWorkspaceSupportType(manifest: IExtensionManifest): ExtensionVirtualWorkspaceSupportType;39}4041export class ExtensionManifestPropertiesService extends Disposable implements IExtensionManifestPropertiesService {4243readonly _serviceBrand: undefined;4445private _extensionPointExtensionKindsMap: Map<string, ExtensionKind[]> | null = null;46private _productExtensionKindsMap: ExtensionIdentifierMap<ExtensionKind[]> | null = null;47private _configuredExtensionKindsMap: ExtensionIdentifierMap<ExtensionKind | ExtensionKind[]> | null = null;4849private _productVirtualWorkspaceSupportMap: ExtensionIdentifierMap<{ default?: boolean; override?: boolean }> | null = null;50private _configuredVirtualWorkspaceSupportMap: ExtensionIdentifierMap<boolean> | null = null;5152private readonly _configuredExtensionWorkspaceTrustRequestMap: ExtensionIdentifierMap<{ supported: ExtensionUntrustedWorkspaceSupportType; version?: string }>;53private readonly _productExtensionWorkspaceTrustRequestMap: Map<string, ExtensionUntrustedWorkspaceSupport>;5455constructor(56@IProductService private readonly productService: IProductService,57@IConfigurationService private readonly configurationService: IConfigurationService,58@IWorkspaceTrustEnablementService private readonly workspaceTrustEnablementService: IWorkspaceTrustEnablementService,59@ILogService private readonly logService: ILogService,60) {61super();6263// Workspace trust request type (settings.json)64this._configuredExtensionWorkspaceTrustRequestMap = new ExtensionIdentifierMap<{ supported: ExtensionUntrustedWorkspaceSupportType; version?: string }>();65const configuredExtensionWorkspaceTrustRequests = configurationService.inspect<{ [key: string]: { supported: ExtensionUntrustedWorkspaceSupportType; version?: string } }>(WORKSPACE_TRUST_EXTENSION_SUPPORT).userValue || {};66for (const id of Object.keys(configuredExtensionWorkspaceTrustRequests)) {67this._configuredExtensionWorkspaceTrustRequestMap.set(id, configuredExtensionWorkspaceTrustRequests[id]);68}6970// Workspace trust request type (product.json)71this._productExtensionWorkspaceTrustRequestMap = new Map<string, ExtensionUntrustedWorkspaceSupport>();72if (productService.extensionUntrustedWorkspaceSupport) {73for (const id of Object.keys(productService.extensionUntrustedWorkspaceSupport)) {74this._productExtensionWorkspaceTrustRequestMap.set(id, productService.extensionUntrustedWorkspaceSupport[id]);75}76}77}7879prefersExecuteOnUI(manifest: IExtensionManifest): boolean {80const extensionKind = this.getExtensionKind(manifest);81return (extensionKind.length > 0 && extensionKind[0] === 'ui');82}8384prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean {85const extensionKind = this.getExtensionKind(manifest);86return (extensionKind.length > 0 && extensionKind[0] === 'workspace');87}8889prefersExecuteOnWeb(manifest: IExtensionManifest): boolean {90const extensionKind = this.getExtensionKind(manifest);91return (extensionKind.length > 0 && extensionKind[0] === 'web');92}9394canExecuteOnUI(manifest: IExtensionManifest): boolean {95const extensionKind = this.getExtensionKind(manifest);96return extensionKind.some(kind => kind === 'ui');97}9899canExecuteOnWorkspace(manifest: IExtensionManifest): boolean {100const extensionKind = this.getExtensionKind(manifest);101return extensionKind.some(kind => kind === 'workspace');102}103104canExecuteOnWeb(manifest: IExtensionManifest): boolean {105const extensionKind = this.getExtensionKind(manifest);106return extensionKind.some(kind => kind === 'web');107}108109getExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {110const deducedExtensionKind = this.deduceExtensionKind(manifest);111const configuredExtensionKind = this.getConfiguredExtensionKind(manifest);112113if (configuredExtensionKind && configuredExtensionKind.length > 0) {114const result: ExtensionKind[] = [];115for (const extensionKind of configuredExtensionKind) {116if (extensionKind !== '-web') {117result.push(extensionKind);118}119}120121// If opted out from web without specifying other extension kinds then default to ui, workspace122if (configuredExtensionKind.includes('-web') && !result.length) {123result.push('ui');124result.push('workspace');125}126127// Add web kind if not opted out from web and can run in web128if (isWeb && !configuredExtensionKind.includes('-web') && !configuredExtensionKind.includes('web') && deducedExtensionKind.includes('web')) {129result.push('web');130}131132return result;133}134135return deducedExtensionKind;136}137138getUserConfiguredExtensionKind(extensionIdentifier: IExtensionIdentifier): ExtensionKind[] | undefined {139if (this._configuredExtensionKindsMap === null) {140const configuredExtensionKindsMap = new ExtensionIdentifierMap<ExtensionKind | ExtensionKind[]>();141const configuredExtensionKinds = this.configurationService.getValue<{ [key: string]: ExtensionKind | ExtensionKind[] }>('remote.extensionKind') || {};142for (const id of Object.keys(configuredExtensionKinds)) {143configuredExtensionKindsMap.set(id, configuredExtensionKinds[id]);144}145this._configuredExtensionKindsMap = configuredExtensionKindsMap;146}147148const userConfiguredExtensionKind = this._configuredExtensionKindsMap.get(extensionIdentifier.id);149return userConfiguredExtensionKind ? this.toArray(userConfiguredExtensionKind) : undefined;150}151152getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupportType {153// Workspace trust feature is disabled, or extension has no entry point154if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled() || !manifest.main) {155return true;156}157158// Get extension workspace trust requirements from settings.json159const configuredWorkspaceTrustRequest = this.getConfiguredExtensionWorkspaceTrustRequest(manifest);160161// Get extension workspace trust requirements from product.json162const productWorkspaceTrustRequest = this.getProductExtensionWorkspaceTrustRequest(manifest);163164// Use settings.json override value if it exists165if (configuredWorkspaceTrustRequest !== undefined) {166return configuredWorkspaceTrustRequest;167}168169// Use product.json override value if it exists170if (productWorkspaceTrustRequest?.override !== undefined) {171return productWorkspaceTrustRequest.override;172}173174// Use extension manifest value if it exists175if (manifest.capabilities?.untrustedWorkspaces?.supported !== undefined) {176return manifest.capabilities.untrustedWorkspaces.supported;177}178179// Use product.json default value if it exists180if (productWorkspaceTrustRequest?.default !== undefined) {181return productWorkspaceTrustRequest.default;182}183184return false;185}186187getExtensionVirtualWorkspaceSupportType(manifest: IExtensionManifest): ExtensionVirtualWorkspaceSupportType {188// check user configured189const userConfiguredVirtualWorkspaceSupport = this.getConfiguredVirtualWorkspaceSupport(manifest);190if (userConfiguredVirtualWorkspaceSupport !== undefined) {191return userConfiguredVirtualWorkspaceSupport;192}193194const productConfiguredWorkspaceSchemes = this.getProductVirtualWorkspaceSupport(manifest);195196// check override from product197if (productConfiguredWorkspaceSchemes?.override !== undefined) {198return productConfiguredWorkspaceSchemes.override;199}200201// check the manifest202const virtualWorkspaces = manifest.capabilities?.virtualWorkspaces;203if (isBoolean(virtualWorkspaces)) {204return virtualWorkspaces;205} else if (virtualWorkspaces) {206const supported = virtualWorkspaces.supported;207if (isBoolean(supported) || supported === 'limited') {208return supported;209}210}211212// check default from product213if (productConfiguredWorkspaceSchemes?.default !== undefined) {214return productConfiguredWorkspaceSchemes.default;215}216217// Default - supports virtual workspace218return true;219}220221private deduceExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {222// Not an UI extension if it has main223if (manifest.main) {224if (manifest.browser) {225return isWeb ? ['workspace', 'web'] : ['workspace'];226}227return ['workspace'];228}229230if (manifest.browser) {231return ['web'];232}233234let result = [...ALL_EXTENSION_KINDS];235236if (isNonEmptyArray(manifest.extensionPack) || isNonEmptyArray(manifest.extensionDependencies)) {237// Extension pack defaults to [workspace, web] in web and only [workspace] in desktop238result = isWeb ? ['workspace', 'web'] : ['workspace'];239}240241if (manifest.contributes) {242for (const contribution of Object.keys(manifest.contributes)) {243const supportedExtensionKinds = this.getSupportedExtensionKindsForExtensionPoint(contribution);244if (supportedExtensionKinds.length) {245result = result.filter(extensionKind => supportedExtensionKinds.includes(extensionKind));246}247}248}249250if (!result.length) {251this.logService.warn('Cannot deduce extensionKind for extension', getGalleryExtensionId(manifest.publisher, manifest.name));252}253254return result;255}256257private getSupportedExtensionKindsForExtensionPoint(extensionPoint: string): ExtensionKind[] {258if (this._extensionPointExtensionKindsMap === null) {259const extensionPointExtensionKindsMap = new Map<string, ExtensionKind[]>();260ExtensionsRegistry.getExtensionPoints().forEach(e => extensionPointExtensionKindsMap.set(e.name, e.defaultExtensionKind || [] /* supports all */));261this._extensionPointExtensionKindsMap = extensionPointExtensionKindsMap;262}263264let extensionPointExtensionKind = this._extensionPointExtensionKindsMap.get(extensionPoint);265if (extensionPointExtensionKind) {266return extensionPointExtensionKind;267}268269extensionPointExtensionKind = this.productService.extensionPointExtensionKind ? this.productService.extensionPointExtensionKind[extensionPoint] : undefined;270if (extensionPointExtensionKind) {271return extensionPointExtensionKind;272}273274/* Unknown extension point */275return isWeb ? ['workspace', 'web'] : ['workspace'];276}277278private getConfiguredExtensionKind(manifest: IExtensionManifest): (ExtensionKind | '-web')[] | null {279const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };280281// check in config282let result: ExtensionKind | ExtensionKind[] | undefined = this.getUserConfiguredExtensionKind(extensionIdentifier);283if (typeof result !== 'undefined') {284return this.toArray(result);285}286287// check product.json288result = this.getProductExtensionKind(manifest);289if (typeof result !== 'undefined') {290return result;291}292293// check the manifest itself294result = manifest.extensionKind;295if (typeof result !== 'undefined') {296result = this.toArray(result);297return result.filter(r => ['ui', 'workspace'].includes(r));298}299300return null;301}302303private getProductExtensionKind(manifest: IExtensionManifest): ExtensionKind[] | undefined {304if (this._productExtensionKindsMap === null) {305const productExtensionKindsMap = new ExtensionIdentifierMap<ExtensionKind[]>();306if (this.productService.extensionKind) {307for (const id of Object.keys(this.productService.extensionKind)) {308productExtensionKindsMap.set(id, this.productService.extensionKind[id]);309}310}311this._productExtensionKindsMap = productExtensionKindsMap;312}313314const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);315return this._productExtensionKindsMap.get(extensionId);316}317318private getProductVirtualWorkspaceSupport(manifest: IExtensionManifest): { default?: boolean; override?: boolean } | undefined {319if (this._productVirtualWorkspaceSupportMap === null) {320const productWorkspaceSchemesMap = new ExtensionIdentifierMap<{ default?: boolean; override?: boolean }>();321if (this.productService.extensionVirtualWorkspacesSupport) {322for (const id of Object.keys(this.productService.extensionVirtualWorkspacesSupport)) {323productWorkspaceSchemesMap.set(id, this.productService.extensionVirtualWorkspacesSupport[id]);324}325}326this._productVirtualWorkspaceSupportMap = productWorkspaceSchemesMap;327}328329const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);330return this._productVirtualWorkspaceSupportMap.get(extensionId);331}332333private getConfiguredVirtualWorkspaceSupport(manifest: IExtensionManifest): boolean | undefined {334if (this._configuredVirtualWorkspaceSupportMap === null) {335const configuredWorkspaceSchemesMap = new ExtensionIdentifierMap<boolean>();336const configuredWorkspaceSchemes = this.configurationService.getValue<{ [key: string]: boolean }>('extensions.supportVirtualWorkspaces') || {};337for (const id of Object.keys(configuredWorkspaceSchemes)) {338if (configuredWorkspaceSchemes[id] !== undefined) {339configuredWorkspaceSchemesMap.set(id, configuredWorkspaceSchemes[id]);340}341}342this._configuredVirtualWorkspaceSupportMap = configuredWorkspaceSchemesMap;343}344345const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);346return this._configuredVirtualWorkspaceSupportMap.get(extensionId);347}348349private getConfiguredExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupportType | undefined {350const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);351const extensionWorkspaceTrustRequest = this._configuredExtensionWorkspaceTrustRequestMap.get(extensionId);352353if (extensionWorkspaceTrustRequest && (extensionWorkspaceTrustRequest.version === undefined || extensionWorkspaceTrustRequest.version === manifest.version)) {354return extensionWorkspaceTrustRequest.supported;355}356357return undefined;358}359360private getProductExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupport | undefined {361const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);362return this._productExtensionWorkspaceTrustRequestMap.get(extensionId);363}364365private toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[] {366if (Array.isArray(extensionKind)) {367return extensionKind;368}369return extensionKind === 'ui' ? ['ui', 'workspace'] : [extensionKind];370}371}372373registerSingleton(IExtensionManifestPropertiesService, ExtensionManifestPropertiesService, InstantiationType.Delayed);374375376