Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/extensionManagement/common/extensionManagementIpc.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 { Emitter, Event } from '../../../base/common/event.js';
7
import { cloneAndChange } from '../../../base/common/objects.js';
8
import { URI, UriComponents } from '../../../base/common/uri.js';
9
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from '../../../base/common/uriIpc.js';
10
import { IChannel, IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
11
import {
12
IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, InstallOptions,
13
UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult,
14
UninstallExtensionEvent, InstallOperation, InstallExtensionInfo, IProductVersion, DidUpdateExtensionMetadata, UninstallExtensionInfo,
15
IAllowedExtensionsService
16
} from './extensionManagement.js';
17
import { ExtensionType, IExtensionManifest, TargetPlatform } from '../../extensions/common/extensions.js';
18
import { IProductService } from '../../product/common/productService.js';
19
import { CommontExtensionManagementService } from './abstractExtensionManagementService.js';
20
import { language } from '../../../base/common/platform.js';
21
22
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;
23
function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined;
24
function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined {
25
return uri ? URI.revive(transformer ? transformer.transformIncoming(uri) : uri) : undefined;
26
}
27
28
function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): URI {
29
return transformer ? transformer.transformOutgoingURI(uri) : uri;
30
}
31
32
function transformIncomingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension {
33
transformer = transformer ? transformer : DefaultURITransformer;
34
const manifest = extension.manifest;
35
const transformed = transformAndReviveIncomingURIs({ ...extension, ...{ manifest: undefined } }, transformer);
36
return { ...transformed, ...{ manifest } };
37
}
38
39
function transformIncomingOptions<O extends { profileLocation?: UriComponents }>(options: O | undefined, transformer: IURITransformer | null): O | undefined {
40
return options?.profileLocation ? transformAndReviveIncomingURIs(options, transformer ?? DefaultURITransformer) : options;
41
}
42
43
function transformOutgoingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension {
44
return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoingURI(value) : undefined) : extension;
45
}
46
47
export class ExtensionManagementChannel implements IServerChannel {
48
49
onInstallExtension: Event<InstallExtensionEvent>;
50
onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;
51
onUninstallExtension: Event<UninstallExtensionEvent>;
52
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
53
onDidUpdateExtensionMetadata: Event<DidUpdateExtensionMetadata>;
54
55
constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) {
56
this.onInstallExtension = Event.buffer(service.onInstallExtension, true);
57
this.onDidInstallExtensions = Event.buffer(service.onDidInstallExtensions, true);
58
this.onUninstallExtension = Event.buffer(service.onUninstallExtension, true);
59
this.onDidUninstallExtension = Event.buffer(service.onDidUninstallExtension, true);
60
this.onDidUpdateExtensionMetadata = Event.buffer(service.onDidUpdateExtensionMetadata, true);
61
}
62
63
listen(context: any, event: string): Event<any> {
64
const uriTransformer = this.getUriTransformer(context);
65
switch (event) {
66
case 'onInstallExtension': {
67
return Event.map<InstallExtensionEvent, InstallExtensionEvent>(this.onInstallExtension, e => {
68
return {
69
...e,
70
profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation
71
};
72
});
73
}
74
case 'onDidInstallExtensions': {
75
return Event.map<readonly InstallExtensionResult[], readonly InstallExtensionResult[]>(this.onDidInstallExtensions, results =>
76
results.map(i => ({
77
...i,
78
local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local,
79
profileLocation: i.profileLocation ? transformOutgoingURI(i.profileLocation, uriTransformer) : i.profileLocation
80
})));
81
}
82
case 'onUninstallExtension': {
83
return Event.map<UninstallExtensionEvent, UninstallExtensionEvent>(this.onUninstallExtension, e => {
84
return {
85
...e,
86
profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation
87
};
88
});
89
}
90
case 'onDidUninstallExtension': {
91
return Event.map<DidUninstallExtensionEvent, DidUninstallExtensionEvent>(this.onDidUninstallExtension, e => {
92
return {
93
...e,
94
profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation
95
};
96
});
97
}
98
case 'onDidUpdateExtensionMetadata': {
99
return Event.map<DidUpdateExtensionMetadata, DidUpdateExtensionMetadata>(this.onDidUpdateExtensionMetadata, e => {
100
return {
101
local: transformOutgoingExtension(e.local, uriTransformer),
102
profileLocation: transformOutgoingURI(e.profileLocation, uriTransformer)
103
};
104
});
105
}
106
}
107
108
throw new Error('Invalid listen');
109
}
110
111
async call(context: any, command: string, args?: any): Promise<any> {
112
const uriTransformer: IURITransformer | null = this.getUriTransformer(context);
113
switch (command) {
114
case 'zip': {
115
const extension = transformIncomingExtension(args[0], uriTransformer);
116
const uri = await this.service.zip(extension);
117
return transformOutgoingURI(uri, uriTransformer);
118
}
119
case 'install': {
120
return this.service.install(transformIncomingURI(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));
121
}
122
case 'installFromLocation': {
123
return this.service.installFromLocation(transformIncomingURI(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));
124
}
125
case 'installExtensionsFromProfile': {
126
return this.service.installExtensionsFromProfile(args[0], transformIncomingURI(args[1], uriTransformer), transformIncomingURI(args[2], uriTransformer));
127
}
128
case 'getManifest': {
129
return this.service.getManifest(transformIncomingURI(args[0], uriTransformer));
130
}
131
case 'getTargetPlatform': {
132
return this.service.getTargetPlatform();
133
}
134
case 'installFromGallery': {
135
return this.service.installFromGallery(args[0], transformIncomingOptions(args[1], uriTransformer));
136
}
137
case 'installGalleryExtensions': {
138
const arg: InstallExtensionInfo[] = args[0];
139
return this.service.installGalleryExtensions(arg.map(({ extension, options }) => ({ extension, options: transformIncomingOptions(options, uriTransformer) ?? {} })));
140
}
141
case 'uninstall': {
142
return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));
143
}
144
case 'uninstallExtensions': {
145
const arg: UninstallExtensionInfo[] = args[0];
146
return this.service.uninstallExtensions(arg.map(({ extension, options }) => ({ extension: transformIncomingExtension(extension, uriTransformer), options: transformIncomingOptions(options, uriTransformer) })));
147
}
148
case 'getInstalled': {
149
const extensions = await this.service.getInstalled(args[0], transformIncomingURI(args[1], uriTransformer), args[2], args[3]);
150
return extensions.map(e => transformOutgoingExtension(e, uriTransformer));
151
}
152
case 'toggleApplicationScope': {
153
const extension = await this.service.toggleApplicationScope(transformIncomingExtension(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));
154
return transformOutgoingExtension(extension, uriTransformer);
155
}
156
case 'copyExtensions': {
157
return this.service.copyExtensions(transformIncomingURI(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));
158
}
159
case 'updateMetadata': {
160
const e = await this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1], transformIncomingURI(args[2], uriTransformer));
161
return transformOutgoingExtension(e, uriTransformer);
162
}
163
case 'resetPinnedStateForAllUserExtensions': {
164
return this.service.resetPinnedStateForAllUserExtensions(args[0]);
165
}
166
case 'getExtensionsControlManifest': {
167
return this.service.getExtensionsControlManifest();
168
}
169
case 'download': {
170
return this.service.download(args[0], args[1], args[2]);
171
}
172
case 'cleanUp': {
173
return this.service.cleanUp();
174
}
175
}
176
177
throw new Error('Invalid call');
178
}
179
}
180
181
export interface ExtensionEventResult {
182
readonly profileLocation: URI;
183
readonly local?: ILocalExtension;
184
readonly applicationScoped?: boolean;
185
}
186
187
export class ExtensionManagementChannelClient extends CommontExtensionManagementService implements IExtensionManagementService {
188
189
declare readonly _serviceBrand: undefined;
190
191
protected readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());
192
get onInstallExtension() { return this._onInstallExtension.event; }
193
194
protected readonly _onDidInstallExtensions = this._register(new Emitter<readonly InstallExtensionResult[]>());
195
get onDidInstallExtensions() { return this._onDidInstallExtensions.event; }
196
197
protected readonly _onUninstallExtension = this._register(new Emitter<UninstallExtensionEvent>());
198
get onUninstallExtension() { return this._onUninstallExtension.event; }
199
200
protected readonly _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
201
get onDidUninstallExtension() { return this._onDidUninstallExtension.event; }
202
203
protected readonly _onDidUpdateExtensionMetadata = this._register(new Emitter<DidUpdateExtensionMetadata>());
204
get onDidUpdateExtensionMetadata() { return this._onDidUpdateExtensionMetadata.event; }
205
206
constructor(
207
private readonly channel: IChannel,
208
productService: IProductService,
209
allowedExtensionsService: IAllowedExtensionsService,
210
) {
211
super(productService, allowedExtensionsService);
212
this._register(this.channel.listen<InstallExtensionEvent>('onInstallExtension')(e => this.onInstallExtensionEvent({ ...e, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })));
213
this._register(this.channel.listen<readonly InstallExtensionResult[]>('onDidInstallExtensions')(results => this.onDidInstallExtensionsEvent(results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })))));
214
this._register(this.channel.listen<UninstallExtensionEvent>('onUninstallExtension')(e => this.onUninstallExtensionEvent({ ...e, profileLocation: URI.revive(e.profileLocation) })));
215
this._register(this.channel.listen<DidUninstallExtensionEvent>('onDidUninstallExtension')(e => this.onDidUninstallExtensionEvent({ ...e, profileLocation: URI.revive(e.profileLocation) })));
216
this._register(this.channel.listen<DidUpdateExtensionMetadata>('onDidUpdateExtensionMetadata')(e => this.onDidUpdateExtensionMetadataEvent({ profileLocation: URI.revive(e.profileLocation), local: transformIncomingExtension(e.local, null) })));
217
}
218
219
protected onInstallExtensionEvent(event: InstallExtensionEvent): void {
220
this._onInstallExtension.fire(event);
221
}
222
223
protected onDidInstallExtensionsEvent(results: readonly InstallExtensionResult[]): void {
224
this._onDidInstallExtensions.fire(results);
225
}
226
227
protected onUninstallExtensionEvent(event: UninstallExtensionEvent): void {
228
this._onUninstallExtension.fire(event);
229
}
230
231
protected onDidUninstallExtensionEvent(event: DidUninstallExtensionEvent): void {
232
this._onDidUninstallExtension.fire(event);
233
}
234
235
protected onDidUpdateExtensionMetadataEvent(event: DidUpdateExtensionMetadata): void {
236
this._onDidUpdateExtensionMetadata.fire(event);
237
}
238
239
private isUriComponents(thing: unknown): thing is UriComponents {
240
if (!thing) {
241
return false;
242
}
243
return typeof (<any>thing).path === 'string' &&
244
typeof (<any>thing).scheme === 'string';
245
}
246
247
protected _targetPlatformPromise: Promise<TargetPlatform> | undefined;
248
getTargetPlatform(): Promise<TargetPlatform> {
249
if (!this._targetPlatformPromise) {
250
this._targetPlatformPromise = this.channel.call<TargetPlatform>('getTargetPlatform');
251
}
252
return this._targetPlatformPromise;
253
}
254
255
zip(extension: ILocalExtension): Promise<URI> {
256
return Promise.resolve(this.channel.call<UriComponents>('zip', [extension]).then(result => URI.revive(result)));
257
}
258
259
install(vsix: URI, options?: InstallOptions): Promise<ILocalExtension> {
260
return Promise.resolve(this.channel.call<ILocalExtension>('install', [vsix, options])).then(local => transformIncomingExtension(local, null));
261
}
262
263
installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension> {
264
return Promise.resolve(this.channel.call<ILocalExtension>('installFromLocation', [location, profileLocation])).then(local => transformIncomingExtension(local, null));
265
}
266
267
async installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]> {
268
const result = await this.channel.call<ILocalExtension[]>('installExtensionsFromProfile', [extensions, fromProfileLocation, toProfileLocation]);
269
return result.map(local => transformIncomingExtension(local, null));
270
}
271
272
getManifest(vsix: URI): Promise<IExtensionManifest> {
273
return Promise.resolve(this.channel.call<IExtensionManifest>('getManifest', [vsix]));
274
}
275
276
installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
277
return Promise.resolve(this.channel.call<ILocalExtension>('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null));
278
}
279
280
async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {
281
const results = await this.channel.call<InstallExtensionResult[]>('installGalleryExtensions', [extensions]);
282
return results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) }));
283
}
284
285
uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {
286
if (extension.isWorkspaceScoped) {
287
throw new Error('Cannot uninstall a workspace extension');
288
}
289
return Promise.resolve(this.channel.call<void>('uninstall', [extension, options]));
290
}
291
292
uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise<void> {
293
if (extensions.some(e => e.extension.isWorkspaceScoped)) {
294
throw new Error('Cannot uninstall a workspace extension');
295
}
296
return Promise.resolve(this.channel.call<void>('uninstallExtensions', [extensions]));
297
298
}
299
300
getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]> {
301
return Promise.resolve(this.channel.call<ILocalExtension[]>('getInstalled', [type, extensionsProfileResource, productVersion, language]))
302
.then(extensions => extensions.map(extension => transformIncomingExtension(extension, null)));
303
}
304
305
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, extensionsProfileResource?: URI): Promise<ILocalExtension> {
306
return Promise.resolve(this.channel.call<ILocalExtension>('updateMetadata', [local, metadata, extensionsProfileResource]))
307
.then(extension => transformIncomingExtension(extension, null));
308
}
309
310
resetPinnedStateForAllUserExtensions(pinned: boolean): Promise<void> {
311
return this.channel.call<void>('resetPinnedStateForAllUserExtensions', [pinned]);
312
}
313
314
toggleApplicationScope(local: ILocalExtension, fromProfileLocation: URI): Promise<ILocalExtension> {
315
return this.channel.call<ILocalExtension>('toggleApplicationScope', [local, fromProfileLocation])
316
.then(extension => transformIncomingExtension(extension, null));
317
}
318
319
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
320
return this.channel.call<void>('copyExtensions', [fromProfileLocation, toProfileLocation]);
321
}
322
323
getExtensionsControlManifest(): Promise<IExtensionsControlManifest> {
324
return Promise.resolve(this.channel.call<IExtensionsControlManifest>('getExtensionsControlManifest'));
325
}
326
327
async download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise<URI> {
328
const result = await this.channel.call<UriComponents>('download', [extension, operation, donotVerifySignature]);
329
return URI.revive(result);
330
}
331
332
async cleanUp(): Promise<void> {
333
return this.channel.call('cleanUp');
334
}
335
336
registerParticipant() { throw new Error('Not Supported'); }
337
}
338
339
export class ExtensionTipsChannel implements IServerChannel {
340
341
constructor(private service: IExtensionTipsService) {
342
}
343
344
listen(context: any, event: string): Event<any> {
345
throw new Error('Invalid listen');
346
}
347
348
call(context: any, command: string, args?: any): Promise<any> {
349
switch (command) {
350
case 'getConfigBasedTips': return this.service.getConfigBasedTips(URI.revive(args[0]));
351
case 'getImportantExecutableBasedTips': return this.service.getImportantExecutableBasedTips();
352
case 'getOtherExecutableBasedTips': return this.service.getOtherExecutableBasedTips();
353
}
354
355
throw new Error('Invalid call');
356
}
357
}
358
359