/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isWeb } from 'vs/base/common/platform'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IWebviewOptions } from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { Webview, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewInputOptions } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape { private static readonly standardSupportedLinkSchemes = new Set([ Schemas.http, Schemas.https, Schemas.mailto, Schemas.vscode, 'vscode-insider', ]); private readonly _proxy: extHostProtocol.ExtHostWebviewsShape; private readonly _webviews = new Map(); constructor( context: extHostProtocol.IExtHostContext, @IOpenerService private readonly _openerService: IOpenerService, @IProductService private readonly _productService: IProductService, ) { super(); this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews); } public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay): void { this._webviews.set(handle, webview); this.hookupWebviewEventDelegate(handle, webview); } public $setHtml(handle: extHostProtocol.WebviewHandle, value: string): void { const webview = this.getWebview(handle); webview.html = value; } public $setOptions(handle: extHostProtocol.WebviewHandle, options: IWebviewOptions): void { const webview = this.getWebview(handle); webview.contentOptions = reviveWebviewOptions(options); } public async $postMessage(handle: extHostProtocol.WebviewHandle, message: any): Promise { const webview = this.getWebview(handle); webview.postMessage(message); return true; } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay) { const disposables = new DisposableStore(); disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri))); disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); disposables.add(webview.onDidDispose(() => { disposables.dispose(); this._webviews.delete(handle); })); } private onDidClickLink(handle: extHostProtocol.WebviewHandle, link: string): void { const webview = this.getWebview(handle); if (this.isSupportedLink(webview, URI.parse(link))) { this._openerService.open(link, { fromUserGesture: true }); } } private isSupportedLink(webview: Webview, link: URI): boolean { if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { return true; } if (!isWeb && this._productService.urlProtocol === link.scheme) { return true; } return !!webview.contentOptions.enableCommandUris && link.scheme === Schemas.command; } private getWebview(handle: extHostProtocol.WebviewHandle): Webview { const webview = this._webviews.get(handle); if (!webview) { throw new Error(`Unknown webview handle:${handle}`); } return webview; } public getWebviewResolvedFailedContent(viewType: string) { return ` ${localize('errorMessage', "An error occurred while loading view: {0}", escape(viewType))} `; } } export function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExtensionDescription): WebviewExtensionDescription { return { id: extensionData.id, location: URI.revive(extensionData.location) }; } export function reviveWebviewOptions(options: IWebviewOptions): WebviewInputOptions { return { ...options, allowScripts: options.enableScripts, localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined, }; }