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