Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/common/extensionManifestPropertiesService.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
7
import { IExtensionManifest, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionIdentifier, ALL_EXTENSION_KINDS, ExtensionIdentifierMap } from '../../../../platform/extensions/common/extensions.js';
8
import { ExtensionKind } from '../../../../platform/environment/common/environment.js';
9
import { ExtensionsRegistry } from './extensionsRegistry.js';
10
import { getGalleryExtensionId } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js';
11
import { isNonEmptyArray } from '../../../../base/common/arrays.js';
12
import { IProductService } from '../../../../platform/product/common/productService.js';
13
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
14
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
15
import { ExtensionUntrustedWorkspaceSupport } from '../../../../base/common/product.js';
16
import { Disposable } from '../../../../base/common/lifecycle.js';
17
import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from '../../workspaces/common/workspaceTrust.js';
18
import { isBoolean } from '../../../../base/common/types.js';
19
import { IWorkspaceTrustEnablementService } from '../../../../platform/workspace/common/workspaceTrust.js';
20
import { ILogService } from '../../../../platform/log/common/log.js';
21
import { isWeb } from '../../../../base/common/platform.js';
22
23
export const IExtensionManifestPropertiesService = createDecorator<IExtensionManifestPropertiesService>('extensionManifestPropertiesService');
24
25
export interface IExtensionManifestPropertiesService {
26
readonly _serviceBrand: undefined;
27
28
prefersExecuteOnUI(manifest: IExtensionManifest): boolean;
29
prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean;
30
prefersExecuteOnWeb(manifest: IExtensionManifest): boolean;
31
32
canExecuteOnUI(manifest: IExtensionManifest): boolean;
33
canExecuteOnWorkspace(manifest: IExtensionManifest): boolean;
34
canExecuteOnWeb(manifest: IExtensionManifest): boolean;
35
36
getExtensionKind(manifest: IExtensionManifest): ExtensionKind[];
37
getUserConfiguredExtensionKind(extensionIdentifier: IExtensionIdentifier): ExtensionKind[] | undefined;
38
getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupportType;
39
getExtensionVirtualWorkspaceSupportType(manifest: IExtensionManifest): ExtensionVirtualWorkspaceSupportType;
40
}
41
42
export class ExtensionManifestPropertiesService extends Disposable implements IExtensionManifestPropertiesService {
43
44
readonly _serviceBrand: undefined;
45
46
private _extensionPointExtensionKindsMap: Map<string, ExtensionKind[]> | null = null;
47
private _productExtensionKindsMap: ExtensionIdentifierMap<ExtensionKind[]> | null = null;
48
private _configuredExtensionKindsMap: ExtensionIdentifierMap<ExtensionKind | ExtensionKind[]> | null = null;
49
50
private _productVirtualWorkspaceSupportMap: ExtensionIdentifierMap<{ default?: boolean; override?: boolean }> | null = null;
51
private _configuredVirtualWorkspaceSupportMap: ExtensionIdentifierMap<boolean> | null = null;
52
53
private readonly _configuredExtensionWorkspaceTrustRequestMap: ExtensionIdentifierMap<{ supported: ExtensionUntrustedWorkspaceSupportType; version?: string }>;
54
private readonly _productExtensionWorkspaceTrustRequestMap: Map<string, ExtensionUntrustedWorkspaceSupport>;
55
56
constructor(
57
@IProductService private readonly productService: IProductService,
58
@IConfigurationService private readonly configurationService: IConfigurationService,
59
@IWorkspaceTrustEnablementService private readonly workspaceTrustEnablementService: IWorkspaceTrustEnablementService,
60
@ILogService private readonly logService: ILogService,
61
) {
62
super();
63
64
// Workspace trust request type (settings.json)
65
this._configuredExtensionWorkspaceTrustRequestMap = new ExtensionIdentifierMap<{ supported: ExtensionUntrustedWorkspaceSupportType; version?: string }>();
66
const configuredExtensionWorkspaceTrustRequests = configurationService.inspect<{ [key: string]: { supported: ExtensionUntrustedWorkspaceSupportType; version?: string } }>(WORKSPACE_TRUST_EXTENSION_SUPPORT).userValue || {};
67
for (const id of Object.keys(configuredExtensionWorkspaceTrustRequests)) {
68
this._configuredExtensionWorkspaceTrustRequestMap.set(id, configuredExtensionWorkspaceTrustRequests[id]);
69
}
70
71
// Workspace trust request type (product.json)
72
this._productExtensionWorkspaceTrustRequestMap = new Map<string, ExtensionUntrustedWorkspaceSupport>();
73
if (productService.extensionUntrustedWorkspaceSupport) {
74
for (const id of Object.keys(productService.extensionUntrustedWorkspaceSupport)) {
75
this._productExtensionWorkspaceTrustRequestMap.set(id, productService.extensionUntrustedWorkspaceSupport[id]);
76
}
77
}
78
}
79
80
prefersExecuteOnUI(manifest: IExtensionManifest): boolean {
81
const extensionKind = this.getExtensionKind(manifest);
82
return (extensionKind.length > 0 && extensionKind[0] === 'ui');
83
}
84
85
prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean {
86
const extensionKind = this.getExtensionKind(manifest);
87
return (extensionKind.length > 0 && extensionKind[0] === 'workspace');
88
}
89
90
prefersExecuteOnWeb(manifest: IExtensionManifest): boolean {
91
const extensionKind = this.getExtensionKind(manifest);
92
return (extensionKind.length > 0 && extensionKind[0] === 'web');
93
}
94
95
canExecuteOnUI(manifest: IExtensionManifest): boolean {
96
const extensionKind = this.getExtensionKind(manifest);
97
return extensionKind.some(kind => kind === 'ui');
98
}
99
100
canExecuteOnWorkspace(manifest: IExtensionManifest): boolean {
101
const extensionKind = this.getExtensionKind(manifest);
102
return extensionKind.some(kind => kind === 'workspace');
103
}
104
105
canExecuteOnWeb(manifest: IExtensionManifest): boolean {
106
const extensionKind = this.getExtensionKind(manifest);
107
return extensionKind.some(kind => kind === 'web');
108
}
109
110
getExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {
111
const deducedExtensionKind = this.deduceExtensionKind(manifest);
112
const configuredExtensionKind = this.getConfiguredExtensionKind(manifest);
113
114
if (configuredExtensionKind && configuredExtensionKind.length > 0) {
115
const result: ExtensionKind[] = [];
116
for (const extensionKind of configuredExtensionKind) {
117
if (extensionKind !== '-web') {
118
result.push(extensionKind);
119
}
120
}
121
122
// If opted out from web without specifying other extension kinds then default to ui, workspace
123
if (configuredExtensionKind.includes('-web') && !result.length) {
124
result.push('ui');
125
result.push('workspace');
126
}
127
128
// Add web kind if not opted out from web and can run in web
129
if (isWeb && !configuredExtensionKind.includes('-web') && !configuredExtensionKind.includes('web') && deducedExtensionKind.includes('web')) {
130
result.push('web');
131
}
132
133
return result;
134
}
135
136
return deducedExtensionKind;
137
}
138
139
getUserConfiguredExtensionKind(extensionIdentifier: IExtensionIdentifier): ExtensionKind[] | undefined {
140
if (this._configuredExtensionKindsMap === null) {
141
const configuredExtensionKindsMap = new ExtensionIdentifierMap<ExtensionKind | ExtensionKind[]>();
142
const configuredExtensionKinds = this.configurationService.getValue<{ [key: string]: ExtensionKind | ExtensionKind[] }>('remote.extensionKind') || {};
143
for (const id of Object.keys(configuredExtensionKinds)) {
144
configuredExtensionKindsMap.set(id, configuredExtensionKinds[id]);
145
}
146
this._configuredExtensionKindsMap = configuredExtensionKindsMap;
147
}
148
149
const userConfiguredExtensionKind = this._configuredExtensionKindsMap.get(extensionIdentifier.id);
150
return userConfiguredExtensionKind ? this.toArray(userConfiguredExtensionKind) : undefined;
151
}
152
153
getExtensionUntrustedWorkspaceSupportType(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupportType {
154
// Workspace trust feature is disabled, or extension has no entry point
155
if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled() || !manifest.main) {
156
return true;
157
}
158
159
// Get extension workspace trust requirements from settings.json
160
const configuredWorkspaceTrustRequest = this.getConfiguredExtensionWorkspaceTrustRequest(manifest);
161
162
// Get extension workspace trust requirements from product.json
163
const productWorkspaceTrustRequest = this.getProductExtensionWorkspaceTrustRequest(manifest);
164
165
// Use settings.json override value if it exists
166
if (configuredWorkspaceTrustRequest !== undefined) {
167
return configuredWorkspaceTrustRequest;
168
}
169
170
// Use product.json override value if it exists
171
if (productWorkspaceTrustRequest?.override !== undefined) {
172
return productWorkspaceTrustRequest.override;
173
}
174
175
// Use extension manifest value if it exists
176
if (manifest.capabilities?.untrustedWorkspaces?.supported !== undefined) {
177
return manifest.capabilities.untrustedWorkspaces.supported;
178
}
179
180
// Use product.json default value if it exists
181
if (productWorkspaceTrustRequest?.default !== undefined) {
182
return productWorkspaceTrustRequest.default;
183
}
184
185
return false;
186
}
187
188
getExtensionVirtualWorkspaceSupportType(manifest: IExtensionManifest): ExtensionVirtualWorkspaceSupportType {
189
// check user configured
190
const userConfiguredVirtualWorkspaceSupport = this.getConfiguredVirtualWorkspaceSupport(manifest);
191
if (userConfiguredVirtualWorkspaceSupport !== undefined) {
192
return userConfiguredVirtualWorkspaceSupport;
193
}
194
195
const productConfiguredWorkspaceSchemes = this.getProductVirtualWorkspaceSupport(manifest);
196
197
// check override from product
198
if (productConfiguredWorkspaceSchemes?.override !== undefined) {
199
return productConfiguredWorkspaceSchemes.override;
200
}
201
202
// check the manifest
203
const virtualWorkspaces = manifest.capabilities?.virtualWorkspaces;
204
if (isBoolean(virtualWorkspaces)) {
205
return virtualWorkspaces;
206
} else if (virtualWorkspaces) {
207
const supported = virtualWorkspaces.supported;
208
if (isBoolean(supported) || supported === 'limited') {
209
return supported;
210
}
211
}
212
213
// check default from product
214
if (productConfiguredWorkspaceSchemes?.default !== undefined) {
215
return productConfiguredWorkspaceSchemes.default;
216
}
217
218
// Default - supports virtual workspace
219
return true;
220
}
221
222
private deduceExtensionKind(manifest: IExtensionManifest): ExtensionKind[] {
223
// Not an UI extension if it has main
224
if (manifest.main) {
225
if (manifest.browser) {
226
return isWeb ? ['workspace', 'web'] : ['workspace'];
227
}
228
return ['workspace'];
229
}
230
231
if (manifest.browser) {
232
return ['web'];
233
}
234
235
let result = [...ALL_EXTENSION_KINDS];
236
237
if (isNonEmptyArray(manifest.extensionPack) || isNonEmptyArray(manifest.extensionDependencies)) {
238
// Extension pack defaults to [workspace, web] in web and only [workspace] in desktop
239
result = isWeb ? ['workspace', 'web'] : ['workspace'];
240
}
241
242
if (manifest.contributes) {
243
for (const contribution of Object.keys(manifest.contributes)) {
244
const supportedExtensionKinds = this.getSupportedExtensionKindsForExtensionPoint(contribution);
245
if (supportedExtensionKinds.length) {
246
result = result.filter(extensionKind => supportedExtensionKinds.includes(extensionKind));
247
}
248
}
249
}
250
251
if (!result.length) {
252
this.logService.warn('Cannot deduce extensionKind for extension', getGalleryExtensionId(manifest.publisher, manifest.name));
253
}
254
255
return result;
256
}
257
258
private getSupportedExtensionKindsForExtensionPoint(extensionPoint: string): ExtensionKind[] {
259
if (this._extensionPointExtensionKindsMap === null) {
260
const extensionPointExtensionKindsMap = new Map<string, ExtensionKind[]>();
261
ExtensionsRegistry.getExtensionPoints().forEach(e => extensionPointExtensionKindsMap.set(e.name, e.defaultExtensionKind || [] /* supports all */));
262
this._extensionPointExtensionKindsMap = extensionPointExtensionKindsMap;
263
}
264
265
let extensionPointExtensionKind = this._extensionPointExtensionKindsMap.get(extensionPoint);
266
if (extensionPointExtensionKind) {
267
return extensionPointExtensionKind;
268
}
269
270
extensionPointExtensionKind = this.productService.extensionPointExtensionKind ? this.productService.extensionPointExtensionKind[extensionPoint] : undefined;
271
if (extensionPointExtensionKind) {
272
return extensionPointExtensionKind;
273
}
274
275
/* Unknown extension point */
276
return isWeb ? ['workspace', 'web'] : ['workspace'];
277
}
278
279
private getConfiguredExtensionKind(manifest: IExtensionManifest): (ExtensionKind | '-web')[] | null {
280
const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) };
281
282
// check in config
283
let result: ExtensionKind | ExtensionKind[] | undefined = this.getUserConfiguredExtensionKind(extensionIdentifier);
284
if (typeof result !== 'undefined') {
285
return this.toArray(result);
286
}
287
288
// check product.json
289
result = this.getProductExtensionKind(manifest);
290
if (typeof result !== 'undefined') {
291
return result;
292
}
293
294
// check the manifest itself
295
result = manifest.extensionKind;
296
if (typeof result !== 'undefined') {
297
result = this.toArray(result);
298
return result.filter(r => ['ui', 'workspace'].includes(r));
299
}
300
301
return null;
302
}
303
304
private getProductExtensionKind(manifest: IExtensionManifest): ExtensionKind[] | undefined {
305
if (this._productExtensionKindsMap === null) {
306
const productExtensionKindsMap = new ExtensionIdentifierMap<ExtensionKind[]>();
307
if (this.productService.extensionKind) {
308
for (const id of Object.keys(this.productService.extensionKind)) {
309
productExtensionKindsMap.set(id, this.productService.extensionKind[id]);
310
}
311
}
312
this._productExtensionKindsMap = productExtensionKindsMap;
313
}
314
315
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
316
return this._productExtensionKindsMap.get(extensionId);
317
}
318
319
private getProductVirtualWorkspaceSupport(manifest: IExtensionManifest): { default?: boolean; override?: boolean } | undefined {
320
if (this._productVirtualWorkspaceSupportMap === null) {
321
const productWorkspaceSchemesMap = new ExtensionIdentifierMap<{ default?: boolean; override?: boolean }>();
322
if (this.productService.extensionVirtualWorkspacesSupport) {
323
for (const id of Object.keys(this.productService.extensionVirtualWorkspacesSupport)) {
324
productWorkspaceSchemesMap.set(id, this.productService.extensionVirtualWorkspacesSupport[id]);
325
}
326
}
327
this._productVirtualWorkspaceSupportMap = productWorkspaceSchemesMap;
328
}
329
330
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
331
return this._productVirtualWorkspaceSupportMap.get(extensionId);
332
}
333
334
private getConfiguredVirtualWorkspaceSupport(manifest: IExtensionManifest): boolean | undefined {
335
if (this._configuredVirtualWorkspaceSupportMap === null) {
336
const configuredWorkspaceSchemesMap = new ExtensionIdentifierMap<boolean>();
337
const configuredWorkspaceSchemes = this.configurationService.getValue<{ [key: string]: boolean }>('extensions.supportVirtualWorkspaces') || {};
338
for (const id of Object.keys(configuredWorkspaceSchemes)) {
339
if (configuredWorkspaceSchemes[id] !== undefined) {
340
configuredWorkspaceSchemesMap.set(id, configuredWorkspaceSchemes[id]);
341
}
342
}
343
this._configuredVirtualWorkspaceSupportMap = configuredWorkspaceSchemesMap;
344
}
345
346
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
347
return this._configuredVirtualWorkspaceSupportMap.get(extensionId);
348
}
349
350
private getConfiguredExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupportType | undefined {
351
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
352
const extensionWorkspaceTrustRequest = this._configuredExtensionWorkspaceTrustRequestMap.get(extensionId);
353
354
if (extensionWorkspaceTrustRequest && (extensionWorkspaceTrustRequest.version === undefined || extensionWorkspaceTrustRequest.version === manifest.version)) {
355
return extensionWorkspaceTrustRequest.supported;
356
}
357
358
return undefined;
359
}
360
361
private getProductExtensionWorkspaceTrustRequest(manifest: IExtensionManifest): ExtensionUntrustedWorkspaceSupport | undefined {
362
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
363
return this._productExtensionWorkspaceTrustRequestMap.get(extensionId);
364
}
365
366
private toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[] {
367
if (Array.isArray(extensionKind)) {
368
return extensionKind;
369
}
370
return extensionKind === 'ui' ? ['ui', 'workspace'] : [extensionKind];
371
}
372
}
373
374
registerSingleton(IExtensionManifestPropertiesService, ExtensionManifestPropertiesService, InstantiationType.Delayed);
375
376