diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index b32b6892411..d69a37ffb0f 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -79,7 +79,6 @@ import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/ import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels'; import { IFileService } from 'vs/platform/files/common/files'; -import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; import { WebviewChannel } from 'vs/platform/webview/electron-main/webviewIpcs'; import { WebviewMainService } from 'vs/platform/webview/electron-main/webviewMainService'; import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; @@ -102,8 +101,6 @@ export class CodeApplication extends Disposable { super(); this.registerListeners(); - - this._register(new WebviewProtocolProvider(fileService)); } private registerListeners(): void { diff --git a/src/vs/platform/webview/common/webviewManagerService.ts b/src/vs/platform/webview/common/webviewManagerService.ts index ebede36f18e..75a021dee8f 100644 --- a/src/vs/platform/webview/common/webviewManagerService.ts +++ b/src/vs/platform/webview/common/webviewManagerService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { UriComponents } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IWebviewManagerService = createDecorator('webviewManagerService'); @@ -10,5 +11,14 @@ export const IWebviewManagerService = createDecorator('w export interface IWebviewManagerService { _serviceBrand: unknown; + registerWebview(id: string, metadata: RegisterWebviewMetadata): Promise; + unregisterWebview(id: string): Promise; + updateLocalResourceRoots(id: string, roots: UriComponents[]): Promise; + setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): void; } + +export interface RegisterWebviewMetadata { + readonly extensionLocation: UriComponents | undefined; + readonly localResourceRoots: readonly UriComponents[]; +} diff --git a/src/vs/platform/webview/electron-main/webviewIpcs.ts b/src/vs/platform/webview/electron-main/webviewIpcs.ts index bc021cb2a4e..7f5aadb9fda 100644 --- a/src/vs/platform/webview/electron-main/webviewIpcs.ts +++ b/src/vs/platform/webview/electron-main/webviewIpcs.ts @@ -7,6 +7,10 @@ import { Event } from 'vs/base/common/event'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +type KeyWithParams = { + [K in keyof T]: T[K] extends (...args: infer P) => any ? [K, P] : never +}[keyof T]; + export class WebviewChannel implements IServerChannel { constructor( @@ -17,11 +21,14 @@ export class WebviewChannel implements IServerChannel { throw new Error(`Event not found: ${event}`); } - async call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'setIgnoreMenuShortcuts': this.webviewMainService.setIgnoreMenuShortcuts(arg[0], arg[1]); return; + async call(_: unknown, ...commandAndArgs: KeyWithParams): Promise { + switch (commandAndArgs[0]) { + case 'setIgnoreMenuShortcuts': this.webviewMainService.setIgnoreMenuShortcuts(...commandAndArgs[1]); return; + case 'registerWebview': this.webviewMainService.registerWebview(...commandAndArgs[1]); return; + case 'unregisterWebview': this.webviewMainService.unregisterWebview(...commandAndArgs[1]); return; + case 'updateLocalResourceRoots': this.webviewMainService.updateLocalResourceRoots(...commandAndArgs[1]); return; } - throw new Error(`Call not found: ${command}`); + throw new Error(`Call not found: ${commandAndArgs[0]}`); } } diff --git a/src/vs/platform/webview/electron-main/webviewMainService.ts b/src/vs/platform/webview/electron-main/webviewMainService.ts index 214cf64fc09..d7b36705579 100644 --- a/src/vs/platform/webview/electron-main/webviewMainService.ts +++ b/src/vs/platform/webview/electron-main/webviewMainService.ts @@ -4,12 +4,38 @@ *--------------------------------------------------------------------------------------------*/ import { webContents } from 'electron'; -import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService'; +import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; +import { IFileService } from 'vs/platform/files/common/files'; +import { UriComponents, URI } from 'vs/base/common/uri'; export class WebviewMainService implements IWebviewManagerService { _serviceBrand: undefined; + private protocolProvider: WebviewProtocolProvider; + + constructor( + @IFileService fileService: IFileService, + ) { + this.protocolProvider = new WebviewProtocolProvider(fileService); + } + + public async registerWebview(id: string, metadata: RegisterWebviewMetadata): Promise { + this.protocolProvider.registerWebview(id, + metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined, + metadata.localResourceRoots.map((x: UriComponents) => URI.from(x)) + ); + } + + public async unregisterWebview(id: string): Promise { + this.protocolProvider.unreigsterWebview(id); + } + + public async updateLocalResourceRoots(id: string, roots: UriComponents[]): Promise { + this.protocolProvider.updateLocalResourceRoots(id, roots.map((x: UriComponents) => URI.from(x))); + } + public setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): void { const contents = webContents.fromId(webContentsId); if (!contents) { diff --git a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts index 0c62c6bee2a..171439f0975 100644 --- a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts +++ b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts @@ -3,25 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcMain as ipc, IpcMainEvent, protocol } from 'electron'; +import { protocol } from 'electron'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { streamToNodeReadable } from 'vs/base/node/stream'; import { IFileService } from 'vs/platform/files/common/files'; import { loadLocalResourceStream, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader'; -export interface RegisterWebviewMetadata { - readonly extensionLocation: URI | undefined; - readonly localResourceRoots: readonly URI[]; -} - - export class WebviewProtocolProvider extends Disposable { private readonly webviewMetadata = new Map(); constructor( @@ -29,30 +23,6 @@ export class WebviewProtocolProvider extends Disposable { ) { super(); - ipc.on('vscode:registerWebview', (event: IpcMainEvent, id: string, data: RegisterWebviewMetadata) => { - this.webviewMetadata.set(id, { - extensionLocation: data.extensionLocation ? URI.from(data.extensionLocation) : undefined, - localResourceRoots: data.localResourceRoots.map((x: UriComponents) => URI.from(x)), - }); - - event.sender.send(`vscode:didRegisterWebview-${id}`); - }); - - ipc.on('vscode:updateWebviewLocalResourceRoots', (event: IpcMainEvent, id: string, localResourceRoots: readonly URI[]) => { - const entry = this.webviewMetadata.get(id); - if (entry) { - this.webviewMetadata.set(id, { - extensionLocation: entry.extensionLocation, - localResourceRoots: localResourceRoots.map((x: UriComponents) => URI.from(x)), - }); - } - event.sender.send(`vscode:didUpdateWebviewLocalResourceRoots-${id}`); - }); - - ipc.on('vscode:unregisterWebview', (_event: IpcMainEvent, id: string) => { - this.webviewMetadata.delete(id); - }); - protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, async (request, callback): Promise => { try { const uri = URI.parse(request.url); @@ -86,4 +56,22 @@ export class WebviewProtocolProvider extends Disposable { this._register(toDisposable(() => protocol.unregisterProtocol(Schemas.vscodeWebviewResource))); } + + public registerWebview(id: string, extensionLocation: URI | undefined, localResourceRoots: readonly URI[]): void { + this.webviewMetadata.set(id, { extensionLocation, localResourceRoots }); + } + + public unreigsterWebview(id: string): void { + this.webviewMetadata.delete(id); + } + + public updateLocalResourceRoots(id: string, localResourceRoots: readonly URI[]) { + const entry = this.webviewMetadata.get(id); + if (entry) { + this.webviewMetadata.set(id, { + extensionLocation: entry.extensionLocation, + localResourceRoots: localResourceRoots, + }); + } + } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 04ad402ed17..cb54735461f 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { FindInPageOptions, OnBeforeRequestListenerDetails, OnHeadersReceivedListenerDetails, Response, WebContents, WebviewTag } from 'electron'; -import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { addDisposableListener } from 'vs/base/browser/dom'; import { equals } from 'vs/base/common/arrays'; import { ThrottledDelayer } from 'vs/base/common/async'; @@ -14,22 +13,22 @@ import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/ import { Schemas } from 'vs/base/common/network'; import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import * as modes from 'vs/editor/common/modes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement'; import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WebviewFindDelegate, WebviewFindWidget } from '../browser/webviewFindWidget'; -import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; -import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; class WebviewTagHandle extends Disposable { @@ -127,19 +126,18 @@ class WebviewProtocolProvider extends Disposable { private readonly id: string, private readonly extension: WebviewExtensionDescription | undefined, initialLocalResourceRoots: ReadonlyArray, + private readonly _webviewManagerService: IWebviewManagerService, ) { super(); this._localResourceRoots = initialLocalResourceRoots; - ipcRenderer.send('vscode:registerWebview', this.id, { + this._ready = _webviewManagerService.registerWebview(this.id, { extensionLocation: this.extension?.location.toJSON(), localResourceRoots: initialLocalResourceRoots.map(x => x.toJSON()), }); - this._ready = new Promise((resolve) => { - ipcRenderer.once(`vscode:didRegisterWebview-${this.id}`, () => resolve()); - }); + this._register(toDisposable(() => this._webviewManagerService.unregisterWebview(this.id))); } public update(localResourceRoots: ReadonlyArray) { @@ -149,22 +147,12 @@ class WebviewProtocolProvider extends Disposable { this._localResourceRoots = localResourceRoots; - ipcRenderer.send('vscode:updateWebviewLocalResourceRoots', this.id, localResourceRoots.map(x => x.toJSON())); - - this._ready = new Promise((resolve) => { - ipcRenderer.once(`vscode:didUpdateWebviewLocalResourceRoots-${this.id}`, () => resolve()); - }); + this._ready = this._webviewManagerService.updateLocalResourceRoots(this.id, localResourceRoots.map(x => x.toJSON())); } async synchronize(): Promise { return this._ready; } - - dispose() { - super.dispose(); - - ipcRenderer.send('vscode:unregisterWebview', this.id); - } } class WebviewPortMappingProvider extends Disposable { @@ -286,10 +274,12 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme ) { super(id, options, contentOptions, extension, _webviewThemeDataProvider, telemetryService, environementService, workbenchEnvironmentService); + const webviewManagerService = createChannelSender(mainProcessService.getChannel('webview')); + const webviewAndContents = this._register(new WebviewTagHandle(this.element!)); const session = this._register(new WebviewSession(webviewAndContents)); - this._protocolProvider = this._register(new WebviewProtocolProvider(id, extension, this.content.options.localResourceRoots || [])); + this._protocolProvider = this._register(new WebviewProtocolProvider(id, extension, this.content.options.localResourceRoots || [], webviewManagerService)); this._register(new WebviewPortMappingProvider( session,