Path: blob/main/src/vs/platform/extensionManagement/common/extensionManagementIpc.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 { Emitter, Event } from '../../../base/common/event.js';6import { cloneAndChange } from '../../../base/common/objects.js';7import { URI, UriComponents } from '../../../base/common/uri.js';8import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from '../../../base/common/uriIpc.js';9import { IChannel, IServerChannel } from '../../../base/parts/ipc/common/ipc.js';10import {11IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, InstallOptions,12UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult,13UninstallExtensionEvent, InstallOperation, InstallExtensionInfo, IProductVersion, DidUpdateExtensionMetadata, UninstallExtensionInfo,14IAllowedExtensionsService15} from './extensionManagement.js';16import { ExtensionType, IExtensionManifest, TargetPlatform } from '../../extensions/common/extensions.js';17import { IProductService } from '../../product/common/productService.js';18import { CommontExtensionManagementService } from './abstractExtensionManagementService.js';19import { language } from '../../../base/common/platform.js';2021function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;22function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined;23function transformIncomingURI(uri: UriComponents | undefined, transformer: IURITransformer | null): URI | undefined {24return uri ? URI.revive(transformer ? transformer.transformIncoming(uri) : uri) : undefined;25}2627function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): URI {28return transformer ? transformer.transformOutgoingURI(uri) : uri;29}3031function transformIncomingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension {32transformer = transformer ? transformer : DefaultURITransformer;33const manifest = extension.manifest;34const transformed = transformAndReviveIncomingURIs({ ...extension, ...{ manifest: undefined } }, transformer);35return { ...transformed, ...{ manifest } };36}3738function transformIncomingOptions<O extends { profileLocation?: UriComponents }>(options: O | undefined, transformer: IURITransformer | null): O | undefined {39return options?.profileLocation ? transformAndReviveIncomingURIs(options, transformer ?? DefaultURITransformer) : options;40}4142function transformOutgoingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension {43return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoingURI(value) : undefined) : extension;44}4546export class ExtensionManagementChannel implements IServerChannel {4748onInstallExtension: Event<InstallExtensionEvent>;49onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;50onUninstallExtension: Event<UninstallExtensionEvent>;51onDidUninstallExtension: Event<DidUninstallExtensionEvent>;52onDidUpdateExtensionMetadata: Event<DidUpdateExtensionMetadata>;5354constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) {55this.onInstallExtension = Event.buffer(service.onInstallExtension, true);56this.onDidInstallExtensions = Event.buffer(service.onDidInstallExtensions, true);57this.onUninstallExtension = Event.buffer(service.onUninstallExtension, true);58this.onDidUninstallExtension = Event.buffer(service.onDidUninstallExtension, true);59this.onDidUpdateExtensionMetadata = Event.buffer(service.onDidUpdateExtensionMetadata, true);60}6162listen(context: any, event: string): Event<any> {63const uriTransformer = this.getUriTransformer(context);64switch (event) {65case 'onInstallExtension': {66return Event.map<InstallExtensionEvent, InstallExtensionEvent>(this.onInstallExtension, e => {67return {68...e,69profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation70};71});72}73case 'onDidInstallExtensions': {74return Event.map<readonly InstallExtensionResult[], readonly InstallExtensionResult[]>(this.onDidInstallExtensions, results =>75results.map(i => ({76...i,77local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local,78profileLocation: i.profileLocation ? transformOutgoingURI(i.profileLocation, uriTransformer) : i.profileLocation79})));80}81case 'onUninstallExtension': {82return Event.map<UninstallExtensionEvent, UninstallExtensionEvent>(this.onUninstallExtension, e => {83return {84...e,85profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation86};87});88}89case 'onDidUninstallExtension': {90return Event.map<DidUninstallExtensionEvent, DidUninstallExtensionEvent>(this.onDidUninstallExtension, e => {91return {92...e,93profileLocation: e.profileLocation ? transformOutgoingURI(e.profileLocation, uriTransformer) : e.profileLocation94};95});96}97case 'onDidUpdateExtensionMetadata': {98return Event.map<DidUpdateExtensionMetadata, DidUpdateExtensionMetadata>(this.onDidUpdateExtensionMetadata, e => {99return {100local: transformOutgoingExtension(e.local, uriTransformer),101profileLocation: transformOutgoingURI(e.profileLocation, uriTransformer)102};103});104}105}106107throw new Error('Invalid listen');108}109110async call(context: any, command: string, args?: any): Promise<any> {111const uriTransformer: IURITransformer | null = this.getUriTransformer(context);112switch (command) {113case 'zip': {114const extension = transformIncomingExtension(args[0], uriTransformer);115const uri = await this.service.zip(extension);116return transformOutgoingURI(uri, uriTransformer);117}118case 'install': {119return this.service.install(transformIncomingURI(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));120}121case 'installFromLocation': {122return this.service.installFromLocation(transformIncomingURI(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));123}124case 'installExtensionsFromProfile': {125return this.service.installExtensionsFromProfile(args[0], transformIncomingURI(args[1], uriTransformer), transformIncomingURI(args[2], uriTransformer));126}127case 'getManifest': {128return this.service.getManifest(transformIncomingURI(args[0], uriTransformer));129}130case 'getTargetPlatform': {131return this.service.getTargetPlatform();132}133case 'installFromGallery': {134return this.service.installFromGallery(args[0], transformIncomingOptions(args[1], uriTransformer));135}136case 'installGalleryExtensions': {137const arg: InstallExtensionInfo[] = args[0];138return this.service.installGalleryExtensions(arg.map(({ extension, options }) => ({ extension, options: transformIncomingOptions(options, uriTransformer) ?? {} })));139}140case 'uninstall': {141return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), transformIncomingOptions(args[1], uriTransformer));142}143case 'uninstallExtensions': {144const arg: UninstallExtensionInfo[] = args[0];145return this.service.uninstallExtensions(arg.map(({ extension, options }) => ({ extension: transformIncomingExtension(extension, uriTransformer), options: transformIncomingOptions(options, uriTransformer) })));146}147case 'getInstalled': {148const extensions = await this.service.getInstalled(args[0], transformIncomingURI(args[1], uriTransformer), args[2], args[3]);149return extensions.map(e => transformOutgoingExtension(e, uriTransformer));150}151case 'toggleApplicationScope': {152const extension = await this.service.toggleApplicationScope(transformIncomingExtension(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));153return transformOutgoingExtension(extension, uriTransformer);154}155case 'copyExtensions': {156return this.service.copyExtensions(transformIncomingURI(args[0], uriTransformer), transformIncomingURI(args[1], uriTransformer));157}158case 'updateMetadata': {159const e = await this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1], transformIncomingURI(args[2], uriTransformer));160return transformOutgoingExtension(e, uriTransformer);161}162case 'resetPinnedStateForAllUserExtensions': {163return this.service.resetPinnedStateForAllUserExtensions(args[0]);164}165case 'getExtensionsControlManifest': {166return this.service.getExtensionsControlManifest();167}168case 'download': {169return this.service.download(args[0], args[1], args[2]);170}171case 'cleanUp': {172return this.service.cleanUp();173}174}175176throw new Error('Invalid call');177}178}179180export interface ExtensionEventResult {181readonly profileLocation: URI;182readonly local?: ILocalExtension;183readonly applicationScoped?: boolean;184}185186export class ExtensionManagementChannelClient extends CommontExtensionManagementService implements IExtensionManagementService {187188declare readonly _serviceBrand: undefined;189190protected readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());191get onInstallExtension() { return this._onInstallExtension.event; }192193protected readonly _onDidInstallExtensions = this._register(new Emitter<readonly InstallExtensionResult[]>());194get onDidInstallExtensions() { return this._onDidInstallExtensions.event; }195196protected readonly _onUninstallExtension = this._register(new Emitter<UninstallExtensionEvent>());197get onUninstallExtension() { return this._onUninstallExtension.event; }198199protected readonly _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());200get onDidUninstallExtension() { return this._onDidUninstallExtension.event; }201202protected readonly _onDidUpdateExtensionMetadata = this._register(new Emitter<DidUpdateExtensionMetadata>());203get onDidUpdateExtensionMetadata() { return this._onDidUpdateExtensionMetadata.event; }204205constructor(206private readonly channel: IChannel,207productService: IProductService,208allowedExtensionsService: IAllowedExtensionsService,209) {210super(productService, allowedExtensionsService);211this._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) })));212this._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) })))));213this._register(this.channel.listen<UninstallExtensionEvent>('onUninstallExtension')(e => this.onUninstallExtensionEvent({ ...e, profileLocation: URI.revive(e.profileLocation) })));214this._register(this.channel.listen<DidUninstallExtensionEvent>('onDidUninstallExtension')(e => this.onDidUninstallExtensionEvent({ ...e, profileLocation: URI.revive(e.profileLocation) })));215this._register(this.channel.listen<DidUpdateExtensionMetadata>('onDidUpdateExtensionMetadata')(e => this.onDidUpdateExtensionMetadataEvent({ profileLocation: URI.revive(e.profileLocation), local: transformIncomingExtension(e.local, null) })));216}217218protected onInstallExtensionEvent(event: InstallExtensionEvent): void {219this._onInstallExtension.fire(event);220}221222protected onDidInstallExtensionsEvent(results: readonly InstallExtensionResult[]): void {223this._onDidInstallExtensions.fire(results);224}225226protected onUninstallExtensionEvent(event: UninstallExtensionEvent): void {227this._onUninstallExtension.fire(event);228}229230protected onDidUninstallExtensionEvent(event: DidUninstallExtensionEvent): void {231this._onDidUninstallExtension.fire(event);232}233234protected onDidUpdateExtensionMetadataEvent(event: DidUpdateExtensionMetadata): void {235this._onDidUpdateExtensionMetadata.fire(event);236}237238private isUriComponents(thing: unknown): thing is UriComponents {239if (!thing) {240return false;241}242return typeof (<any>thing).path === 'string' &&243typeof (<any>thing).scheme === 'string';244}245246protected _targetPlatformPromise: Promise<TargetPlatform> | undefined;247getTargetPlatform(): Promise<TargetPlatform> {248if (!this._targetPlatformPromise) {249this._targetPlatformPromise = this.channel.call<TargetPlatform>('getTargetPlatform');250}251return this._targetPlatformPromise;252}253254zip(extension: ILocalExtension): Promise<URI> {255return Promise.resolve(this.channel.call<UriComponents>('zip', [extension]).then(result => URI.revive(result)));256}257258install(vsix: URI, options?: InstallOptions): Promise<ILocalExtension> {259return Promise.resolve(this.channel.call<ILocalExtension>('install', [vsix, options])).then(local => transformIncomingExtension(local, null));260}261262installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension> {263return Promise.resolve(this.channel.call<ILocalExtension>('installFromLocation', [location, profileLocation])).then(local => transformIncomingExtension(local, null));264}265266async installExtensionsFromProfile(extensions: IExtensionIdentifier[], fromProfileLocation: URI, toProfileLocation: URI): Promise<ILocalExtension[]> {267const result = await this.channel.call<ILocalExtension[]>('installExtensionsFromProfile', [extensions, fromProfileLocation, toProfileLocation]);268return result.map(local => transformIncomingExtension(local, null));269}270271getManifest(vsix: URI): Promise<IExtensionManifest> {272return Promise.resolve(this.channel.call<IExtensionManifest>('getManifest', [vsix]));273}274275installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {276return Promise.resolve(this.channel.call<ILocalExtension>('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null));277}278279async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {280const results = await this.channel.call<InstallExtensionResult[]>('installGalleryExtensions', [extensions]);281return 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) }));282}283284uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {285if (extension.isWorkspaceScoped) {286throw new Error('Cannot uninstall a workspace extension');287}288return Promise.resolve(this.channel.call<void>('uninstall', [extension, options]));289}290291uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise<void> {292if (extensions.some(e => e.extension.isWorkspaceScoped)) {293throw new Error('Cannot uninstall a workspace extension');294}295return Promise.resolve(this.channel.call<void>('uninstallExtensions', [extensions]));296297}298299getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]> {300return Promise.resolve(this.channel.call<ILocalExtension[]>('getInstalled', [type, extensionsProfileResource, productVersion, language]))301.then(extensions => extensions.map(extension => transformIncomingExtension(extension, null)));302}303304updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, extensionsProfileResource?: URI): Promise<ILocalExtension> {305return Promise.resolve(this.channel.call<ILocalExtension>('updateMetadata', [local, metadata, extensionsProfileResource]))306.then(extension => transformIncomingExtension(extension, null));307}308309resetPinnedStateForAllUserExtensions(pinned: boolean): Promise<void> {310return this.channel.call<void>('resetPinnedStateForAllUserExtensions', [pinned]);311}312313toggleApplicationScope(local: ILocalExtension, fromProfileLocation: URI): Promise<ILocalExtension> {314return this.channel.call<ILocalExtension>('toggleApplicationScope', [local, fromProfileLocation])315.then(extension => transformIncomingExtension(extension, null));316}317318copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {319return this.channel.call<void>('copyExtensions', [fromProfileLocation, toProfileLocation]);320}321322getExtensionsControlManifest(): Promise<IExtensionsControlManifest> {323return Promise.resolve(this.channel.call<IExtensionsControlManifest>('getExtensionsControlManifest'));324}325326async download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise<URI> {327const result = await this.channel.call<UriComponents>('download', [extension, operation, donotVerifySignature]);328return URI.revive(result);329}330331async cleanUp(): Promise<void> {332return this.channel.call('cleanUp');333}334335registerParticipant() { throw new Error('Not Supported'); }336}337338export class ExtensionTipsChannel implements IServerChannel {339340constructor(private service: IExtensionTipsService) {341}342343listen(context: any, event: string): Event<any> {344throw new Error('Invalid listen');345}346347call(context: any, command: string, args?: any): Promise<any> {348switch (command) {349case 'getConfigBasedTips': return this.service.getConfigBasedTips(URI.revive(args[0]));350case 'getImportantExecutableBasedTips': return this.service.getImportantExecutableBasedTips();351case 'getOtherExecutableBasedTips': return this.service.getOtherExecutableBasedTips();352}353354throw new Error('Invalid call');355}356}357358359