/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { WebContents, webContents, WebFrameMain } from 'electron'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { FindInFrameOptions, FoundInFrameResult, IWebviewManagerService, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; export class WebviewMainService extends Disposable implements IWebviewManagerService { declare readonly _serviceBrand: undefined; private readonly _onFoundInFrame = this._register(new Emitter()); public onFoundInFrame = this._onFoundInFrame.event; constructor( @IWindowsMainService private readonly windowsMainService: IWindowsMainService, ) { super(); this._register(new WebviewProtocolProvider()); } public async setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise { let contents: WebContents | undefined; if (typeof (id as WebviewWindowId).windowId === 'number') { const { windowId } = (id as WebviewWindowId); const window = this.windowsMainService.getWindowById(windowId); if (!window?.win) { throw new Error(`Invalid windowId: ${windowId}`); } contents = window.win.webContents; } else { const { webContentsId } = (id as WebviewWebContentsId); contents = webContents.fromId(webContentsId); if (!contents) { throw new Error(`Invalid webContentsId: ${webContentsId}`); } } if (!contents.isDestroyed()) { contents.setIgnoreMenuShortcuts(enabled); } } public async findInFrame(windowId: WebviewWindowId, frameName: string, text: string, options: { findNext?: boolean; forward?: boolean }): Promise { const initialFrame = this.getFrameByName(windowId, frameName); type WebFrameMainWithFindSupport = WebFrameMain & { findInFrame?(text: string, findOptions: FindInFrameOptions): void; on(event: 'found-in-frame', listener: Function): WebFrameMain; removeListener(event: 'found-in-frame', listener: Function): WebFrameMain; }; const frame = initialFrame as unknown as WebFrameMainWithFindSupport; if (typeof frame.findInFrame === 'function') { frame.findInFrame(text, { findNext: options.findNext, forward: options.forward, }); const foundInFrameHandler = (_: unknown, result: FoundInFrameResult) => { if (result.finalUpdate) { this._onFoundInFrame.fire(result); frame.removeListener('found-in-frame', foundInFrameHandler); } }; frame.on('found-in-frame', foundInFrameHandler); } } public async stopFindInFrame(windowId: WebviewWindowId, frameName: string, options: { keepSelection?: boolean }): Promise { const initialFrame = this.getFrameByName(windowId, frameName); type WebFrameMainWithFindSupport = WebFrameMain & { stopFindInFrame?(stopOption: 'keepSelection' | 'clearSelection'): void; }; const frame = initialFrame as unknown as WebFrameMainWithFindSupport; if (typeof frame.stopFindInFrame === 'function') { frame.stopFindInFrame(options.keepSelection ? 'keepSelection' : 'clearSelection'); } } private getFrameByName(windowId: WebviewWindowId, frameName: string): WebFrameMain { const window = this.windowsMainService.getWindowById(windowId.windowId); if (!window?.win) { throw new Error(`Invalid windowId: ${windowId}`); } const frame = window.win.webContents.mainFrame.framesInSubtree.find(frame => { return frame.name === frameName; }); if (!frame) { throw new Error(`Unknown frame: ${frameName}`); } return frame; } }