Add experimental support for using iframes for webviews (#100991)
* Use non-deprecated API version * Prototype: enable offline iframe based webviews on desktop Adds a new `vscode-webview` protocol for loading the wrapper contents of a webview Still needs cleaning up * fix loading of electron webview * Cleanup and add `webview.experimental.useIframes` setting Co-authored-by: deepak1556 <hop2deep@gmail.com>
This commit is contained in:
parent
16be2c9885
commit
48c6e3979d
|
@ -32,7 +32,7 @@ interface StandardTsServerRequests {
|
|||
'completions': [Proto.CompletionsRequestArgs, Proto.CompletionsResponse];
|
||||
'configure': [Proto.ConfigureRequestArguments, Proto.ConfigureResponse];
|
||||
'definition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse];
|
||||
'definitionAndBoundSpan': [Proto.FileLocationRequestArgs, Proto.DefinitionInfoAndBoundSpanReponse];
|
||||
'definitionAndBoundSpan': [Proto.FileLocationRequestArgs, Proto.DefinitionInfoAndBoundSpanResponse];
|
||||
'docCommentTemplate': [Proto.FileLocationRequestArgs, Proto.DocCommandTemplateResponse];
|
||||
'documentHighlights': [Proto.DocumentHighlightsRequestArgs, Proto.DocumentHighlightsResponse];
|
||||
'format': [Proto.FormatRequestArgs, Proto.FormatResponse];
|
||||
|
|
|
@ -86,8 +86,9 @@ setCurrentWorkingDirectory();
|
|||
// Register custom schemes with privileges
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: 'vscode-resource',
|
||||
scheme: 'vscode-webview',
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true,
|
||||
|
|
|
@ -62,6 +62,14 @@ export namespace Schemas {
|
|||
|
||||
export const webviewPanel = 'webview-panel';
|
||||
|
||||
/**
|
||||
* Scheme used for loading the wrapper html and script in webviews.
|
||||
*/
|
||||
export const vscodeWebview = 'vscode-webview';
|
||||
|
||||
/**
|
||||
* Scheme used for loading resources inside of webviews.
|
||||
*/
|
||||
export const vscodeWebviewResource = 'vscode-webview-resource';
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote-resource:;">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview: https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote-resource:;">
|
||||
</head>
|
||||
<body aria-label="">
|
||||
</body>
|
||||
|
|
|
@ -174,11 +174,12 @@ export class CodeApplication extends Disposable {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%20role%3D%22document%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
|
||||
return true;
|
||||
const uri = URI.parse(source);
|
||||
if (uri.scheme === Schemas.vscodeWebview) {
|
||||
return uri.path === '/index.html' || uri.path === '/electron-browser/index.html';
|
||||
}
|
||||
|
||||
const srcUri = URI.parse(source).fsPath.toLowerCase();
|
||||
const srcUri = uri.fsPath.toLowerCase();
|
||||
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
|
||||
|
||||
return srcUri.startsWith(rootUri + sep);
|
||||
|
|
|
@ -13,7 +13,7 @@ export const IWebviewManagerService = createDecorator<IWebviewManagerService>('w
|
|||
export interface IWebviewManagerService {
|
||||
_serviceBrand: unknown;
|
||||
|
||||
registerWebview(id: string, webContentsId: number, metadata: RegisterWebviewMetadata): Promise<void>;
|
||||
registerWebview(id: string, webContentsId: number | undefined, metadata: RegisterWebviewMetadata): Promise<void>;
|
||||
unregisterWebview(id: string): Promise<void>;
|
||||
updateWebviewMetadata(id: string, metadataDelta: Partial<RegisterWebviewMetadata>): Promise<void>;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
|
|||
this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService));
|
||||
}
|
||||
|
||||
public async registerWebview(id: string, webContentsId: number, metadata: RegisterWebviewMetadata): Promise<void> {
|
||||
public async registerWebview(id: string, webContentsId: number | undefined, metadata: RegisterWebviewMetadata): Promise<void> {
|
||||
const extensionLocation = metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined;
|
||||
|
||||
this.protocolProvider.registerWebview(id, {
|
||||
|
|
|
@ -20,7 +20,7 @@ interface PortMappingData {
|
|||
export class WebviewPortMappingProvider extends Disposable {
|
||||
|
||||
private readonly _webviewData = new Map<string, {
|
||||
readonly webContentsId: number;
|
||||
readonly webContentsId: number | undefined;
|
||||
readonly manager: WebviewPortMappingManager;
|
||||
metadata: PortMappingData;
|
||||
}>();
|
||||
|
@ -56,14 +56,16 @@ export class WebviewPortMappingProvider extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
public async registerWebview(id: string, webContentsId: number, metadata: PortMappingData): Promise<void> {
|
||||
public async registerWebview(id: string, webContentsId: number | undefined, metadata: PortMappingData): Promise<void> {
|
||||
const manager = new WebviewPortMappingManager(
|
||||
() => this._webviewData.get(id)?.metadata.extensionLocation,
|
||||
() => this._webviewData.get(id)?.metadata.mappings || [],
|
||||
this._tunnelService);
|
||||
|
||||
this._webviewData.set(id, { webContentsId, metadata, manager });
|
||||
this._webContentsIdsToWebviewIds.set(webContentsId, id);
|
||||
if (typeof webContentsId === 'number') {
|
||||
this._webContentsIdsToWebviewIds.set(webContentsId, id);
|
||||
}
|
||||
}
|
||||
|
||||
public unregisterWebview(id: string): void {
|
||||
|
@ -71,7 +73,9 @@ export class WebviewPortMappingProvider extends Disposable {
|
|||
if (existing) {
|
||||
existing.manager.dispose();
|
||||
this._webviewData.delete(id);
|
||||
this._webContentsIdsToWebviewIds.delete(existing.webContentsId);
|
||||
if (typeof existing.webContentsId === 'number') {
|
||||
this._webContentsIdsToWebviewIds.delete(existing.webContentsId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { session } from 'electron';
|
||||
import { session, protocol } from 'electron';
|
||||
import { Readable } from 'stream';
|
||||
import { VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -23,6 +23,13 @@ interface WebviewMetadata {
|
|||
|
||||
export class WebviewProtocolProvider extends Disposable {
|
||||
|
||||
private static validWebviewFilePaths = new Map([
|
||||
['/index.html', 'index.html'],
|
||||
['/electron-browser/index.html', 'index.html'],
|
||||
['/main.js', 'main.js'],
|
||||
['/host.js', 'host.js'],
|
||||
]);
|
||||
|
||||
private readonly webviewMetadata = new Map<string, WebviewMetadata>();
|
||||
|
||||
constructor(
|
||||
|
@ -33,62 +40,22 @@ export class WebviewProtocolProvider extends Disposable {
|
|||
|
||||
const sess = session.fromPartition(webviewPartitionId);
|
||||
|
||||
sess.protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, async (request, callback): Promise<void> => {
|
||||
try {
|
||||
const uri = URI.parse(request.url);
|
||||
// Register the protocol loading webview html
|
||||
const webviewHandler = this.handleWebviewRequest.bind(this);
|
||||
protocol.registerFileProtocol(Schemas.vscodeWebview, webviewHandler);
|
||||
sess.protocol.registerFileProtocol(Schemas.vscodeWebview, webviewHandler);
|
||||
|
||||
const id = uri.authority;
|
||||
const metadata = this.webviewMetadata.get(id);
|
||||
if (metadata) {
|
||||
// Register the protocol loading webview resources both inside the webview and at the top level
|
||||
const webviewResourceHandler = this.handleWebviewResourceRequest.bind(this);
|
||||
protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, webviewResourceHandler);
|
||||
sess.protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, webviewResourceHandler);
|
||||
|
||||
// Try to further rewrite remote uris so that they go to the resolved server on the main thread
|
||||
let rewriteUri: undefined | ((uri: URI) => URI);
|
||||
if (metadata.remoteConnectionData) {
|
||||
rewriteUri = (uri) => {
|
||||
if (metadata.remoteConnectionData) {
|
||||
if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) {
|
||||
const scheme = metadata.remoteConnectionData.host === 'localhost' || metadata.remoteConnectionData.host === '127.0.0.1' ? 'http' : 'https';
|
||||
return URI.parse(`${scheme}://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({
|
||||
path: '/vscode-remote-resource',
|
||||
query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
}
|
||||
|
||||
const result = await loadLocalResource(uri, {
|
||||
extensionLocation: metadata.extensionLocation,
|
||||
roots: metadata.localResourceRoots,
|
||||
remoteConnectionData: metadata.remoteConnectionData,
|
||||
rewriteUri,
|
||||
}, this.fileService, this.requestService);
|
||||
|
||||
if (result.type === WebviewResourceResponse.Type.Success) {
|
||||
return callback({
|
||||
statusCode: 200,
|
||||
data: this.streamToNodeReadable(result.stream),
|
||||
headers: {
|
||||
'Content-Type': result.mimeType,
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result.type === WebviewResourceResponse.Type.AccessDenied) {
|
||||
console.error('Webview: Cannot load resource outside of protocol root');
|
||||
return callback({ data: null, statusCode: 401 });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
return callback({ data: null, statusCode: 404 });
|
||||
});
|
||||
|
||||
this._register(toDisposable(() => sess.protocol.unregisterProtocol(Schemas.vscodeWebviewResource)));
|
||||
this._register(toDisposable(() => {
|
||||
protocol.unregisterProtocol(Schemas.vscodeWebviewResource);
|
||||
sess.protocol.unregisterProtocol(Schemas.vscodeWebviewResource);
|
||||
protocol.unregisterProtocol(Schemas.vscodeWebview);
|
||||
sess.protocol.unregisterProtocol(Schemas.vscodeWebview);
|
||||
}));
|
||||
}
|
||||
|
||||
private streamToNodeReadable(stream: VSBufferReadableStream): Readable {
|
||||
|
@ -152,4 +119,81 @@ export class WebviewProtocolProvider extends Disposable {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleWebviewRequest(request: Electron.Request, callback: any) {
|
||||
try {
|
||||
const uri = URI.parse(request.url);
|
||||
const entry = WebviewProtocolProvider.validWebviewFilePaths.get(uri.path);
|
||||
if (typeof entry === 'string') {
|
||||
let url: string;
|
||||
if (uri.path.startsWith('/electron-browser')) {
|
||||
url = require.toUrl(`vs/workbench/contrib/webview/electron-browser/pre/${entry}`);
|
||||
} else {
|
||||
url = require.toUrl(`vs/workbench/contrib/webview/browser/pre/${entry}`);
|
||||
}
|
||||
return callback(url.replace('file://', ''));
|
||||
}
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
callback({ error: -10 /* ACCESS_DENIED - https://cs.chromium.org/chromium/src/net/base/net_error_list.h?l=32 */ });
|
||||
}
|
||||
|
||||
private async handleWebviewResourceRequest(
|
||||
request: Electron.Request,
|
||||
callback: (stream?: NodeJS.ReadableStream | Electron.StreamProtocolResponse | undefined) => void
|
||||
) {
|
||||
try {
|
||||
const uri = URI.parse(request.url);
|
||||
|
||||
const id = uri.authority;
|
||||
const metadata = this.webviewMetadata.get(id);
|
||||
if (metadata) {
|
||||
|
||||
// Try to further rewrite remote uris so that they go to the resolved server on the main thread
|
||||
let rewriteUri: undefined | ((uri: URI) => URI);
|
||||
if (metadata.remoteConnectionData) {
|
||||
rewriteUri = (uri) => {
|
||||
if (metadata.remoteConnectionData) {
|
||||
if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) {
|
||||
const scheme = metadata.remoteConnectionData.host === 'localhost' || metadata.remoteConnectionData.host === '127.0.0.1' ? 'http' : 'https';
|
||||
return URI.parse(`${scheme}://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({
|
||||
path: '/vscode-remote-resource',
|
||||
query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
}
|
||||
|
||||
const result = await loadLocalResource(uri, {
|
||||
extensionLocation: metadata.extensionLocation,
|
||||
roots: metadata.localResourceRoots,
|
||||
remoteConnectionData: metadata.remoteConnectionData,
|
||||
rewriteUri,
|
||||
}, this.fileService, this.requestService);
|
||||
|
||||
if (result.type === WebviewResourceResponse.Type.Success) {
|
||||
return callback({
|
||||
statusCode: 200,
|
||||
data: this.streamToNodeReadable(result.stream),
|
||||
headers: {
|
||||
'Content-Type': result.mimeType,
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (result.type === WebviewResourceResponse.Type.AccessDenied) {
|
||||
console.error('Webview: Cannot load resource outside of protocol root');
|
||||
return callback({ data: null, statusCode: 401 });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
|
||||
return callback({ data: null, statusCode: 404 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// @ts-check
|
||||
(function () {
|
||||
const id = document.location.search.match(/\bid=([\w-]+)/)[1];
|
||||
const onElectron = /platform=electron/.test(document.location.search);
|
||||
|
||||
const hostMessaging = new class HostMessaging {
|
||||
constructor() {
|
||||
|
@ -36,6 +37,10 @@
|
|||
}();
|
||||
|
||||
const workerReady = new Promise(async (resolveWorkerReady) => {
|
||||
if (onElectron) {
|
||||
return resolveWorkerReady();
|
||||
}
|
||||
|
||||
if (!areServiceWorkersEnabled()) {
|
||||
console.log('Service Workers are not enabled. Webviews will not work properly');
|
||||
return resolveWorkerReady();
|
||||
|
@ -95,11 +100,15 @@
|
|||
postMessage: hostMessaging.postMessage.bind(hostMessaging),
|
||||
onMessage: hostMessaging.onMessage.bind(hostMessaging),
|
||||
ready: workerReady,
|
||||
fakeLoad: true,
|
||||
rewriteCSP: (csp, endpoint) => {
|
||||
const endpointUrl = new URL(endpoint);
|
||||
return csp.replace(/(vscode-webview-resource|vscode-resource):(?=(\s|;|$))/g, endpointUrl.origin);
|
||||
}
|
||||
fakeLoad: !onElectron,
|
||||
rewriteCSP: onElectron
|
||||
? (csp) => {
|
||||
return csp.replace(/vscode-resource:(?=(\s|;|$))/g, 'vscode-webview-resource:');
|
||||
}
|
||||
: (csp, endpoint) => {
|
||||
const endpointUrl = new URL(endpoint);
|
||||
return csp.replace(/(vscode-webview-resource|vscode-resource):(?=(\s|;|$))/g, endpointUrl.origin);
|
||||
}
|
||||
};
|
||||
|
||||
(/** @type {any} */ (window)).createWebviewManager(host);
|
||||
|
|
|
@ -7,9 +7,7 @@ import { addDisposableListener } from 'vs/base/browser/dom';
|
|||
import { streamToBuffer } from 'vs/base/common/buffer';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
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 { ILogService } from 'vs/platform/log/common/log';
|
||||
|
@ -37,7 +35,6 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
@ITunnelService tunnelService: ITunnelService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IRequestService private readonly requestService: IRequestService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IWorkbenchEnvironmentService private readonly _workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
|
@ -46,10 +43,6 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
) {
|
||||
super(id, options, contentOptions, extension, webviewThemeDataProvider, logService, telemetryService, environmentService, _workbenchEnvironmentService);
|
||||
|
||||
if (!this.useExternalEndpoint && (!_workbenchEnvironmentService.options || typeof _workbenchEnvironmentService.webviewExternalEndpoint !== 'string')) {
|
||||
throw new Error('To use iframe based webviews, you must configure `environmentService.webviewExternalEndpoint`');
|
||||
}
|
||||
|
||||
this._portMappingManager = this._register(new WebviewPortMappingManager(
|
||||
() => this.extension?.location,
|
||||
() => this.content.options.portMapping || [],
|
||||
|
@ -67,8 +60,7 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
this.localLocalhost(entry.origin);
|
||||
}));
|
||||
|
||||
// The extensionId and purpose in the URL are used for filtering in js-debug:
|
||||
this.element!.setAttribute('src', `${this.externalEndpoint}/index.html?id=${this.id}&extensionId=${extension?.id.value ?? ''}&purpose=${options.purpose}`);
|
||||
this.initElement(extension, options);
|
||||
}
|
||||
|
||||
protected createElement(options: WebviewOptions, _contentOptions: WebviewContentOptions) {
|
||||
|
@ -83,6 +75,11 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
return element;
|
||||
}
|
||||
|
||||
protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions) {
|
||||
// The extensionId and purpose in the URL are used for filtering in js-debug:
|
||||
this.element!.setAttribute('src', `${this.externalEndpoint}/index.html?id=${this.id}&extensionId=${extension?.id.value ?? ''}&purpose=${options.purpose}`);
|
||||
}
|
||||
|
||||
private get externalEndpoint(): string {
|
||||
const endpoint = this.workbenchEnvironmentService.webviewExternalEndpoint!.replace('{{uuid}}', this.id);
|
||||
if (endpoint[endpoint.length - 1] === '/') {
|
||||
|
@ -91,10 +88,6 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
return endpoint;
|
||||
}
|
||||
|
||||
private get useExternalEndpoint(): boolean {
|
||||
return isWeb || this._configurationService.getValue<boolean>('webview.experimental.useExternalEndpoint');
|
||||
}
|
||||
|
||||
public mountTo(parent: HTMLElement) {
|
||||
if (this.element) {
|
||||
parent.appendChild(this.element);
|
||||
|
@ -105,7 +98,7 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
super.html = this.preprocessHtml(value);
|
||||
}
|
||||
|
||||
private preprocessHtml(value: string): string {
|
||||
protected preprocessHtml(value: string): string {
|
||||
return value
|
||||
.replace(/(["'])(?:vscode-resource):(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (match, startQuote, _1, scheme, path, endQuote) => {
|
||||
if (scheme) {
|
||||
|
@ -121,7 +114,7 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
|||
});
|
||||
}
|
||||
|
||||
protected get extraContentOptions() {
|
||||
protected get extraContentOptions(): any {
|
||||
return {
|
||||
endpoint: this.externalEndpoint,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
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 { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing';
|
||||
import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement';
|
||||
import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-browser/resourceLoading';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
/**
|
||||
* Webview backed by an iframe but that uses Electron APIs to power the webview.
|
||||
*/
|
||||
export class ElectronIframeWebview extends IFrameWebview {
|
||||
|
||||
private readonly _resourceRequestManager: WebviewResourceRequestManager;
|
||||
private _messagePromise = Promise.resolve();
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
options: WebviewOptions,
|
||||
contentOptions: WebviewContentOptions,
|
||||
extension: WebviewExtensionDescription | undefined,
|
||||
webviewThemeDataProvider: WebviewThemeDataProvider,
|
||||
@ITunnelService tunnelService: ITunnelService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IRequestService requestService: IRequestService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IWorkbenchEnvironmentService _workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IRemoteAuthorityResolverService _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@ILogService logService: ILogService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
super(id, options, contentOptions, extension, webviewThemeDataProvider,
|
||||
tunnelService, fileService, requestService, telemetryService, environmentService, _workbenchEnvironmentService, _remoteAuthorityResolverService, logService);
|
||||
|
||||
this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options, Promise.resolve(undefined)));
|
||||
}
|
||||
|
||||
protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions) {
|
||||
// The extensionId and purpose in the URL are used for filtering in js-debug:
|
||||
this.element!.setAttribute('src', `${Schemas.vscodeWebview}://${this.id}/index.html?id=${this.id}&platform=electron&extensionId=${extension?.id.value ?? ''}&purpose=${options.purpose}`);
|
||||
}
|
||||
|
||||
public set contentOptions(options: WebviewContentOptions) {
|
||||
this._resourceRequestManager.update(options);
|
||||
super.contentOptions = options;
|
||||
}
|
||||
|
||||
public set localResourcesRoot(resources: URI[]) {
|
||||
this._resourceRequestManager.update({
|
||||
...this.contentOptions,
|
||||
localResourceRoots: resources,
|
||||
});
|
||||
super.localResourcesRoot = resources;
|
||||
}
|
||||
|
||||
protected get extraContentOptions() {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected async doPostMessage(channel: string, data?: any): Promise<void> {
|
||||
this._messagePromise = this._messagePromise
|
||||
.then(() => this._resourceRequestManager.ensureReady())
|
||||
.then(() => {
|
||||
this.element?.contentWindow!.postMessage({ channel, args: data }, '*');
|
||||
});
|
||||
}
|
||||
|
||||
protected preprocessHtml(value: string): string {
|
||||
return rewriteVsCodeResourceUrls(this.id, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" style="width: 100%; height: 100%">
|
||||
<head>
|
||||
<title>Virtual Document</title>
|
||||
</head>
|
||||
<body style="margin: 0; overflow: hidden; width: 100%; height: 100%" role="document">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,134 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
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 { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
|
||||
import { WebviewContentOptions, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
/**
|
||||
* Try to rewrite `vscode-resource:` urls in html
|
||||
*/
|
||||
export function rewriteVsCodeResourceUrls(
|
||||
id: string,
|
||||
html: string,
|
||||
): string {
|
||||
return html
|
||||
.replace(/(["'])vscode-resource:(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => {
|
||||
if (scheme) {
|
||||
return `${startQuote}${Schemas.vscodeWebviewResource}://${id}/${scheme}${path}${endQuote}`;
|
||||
}
|
||||
if (!path.startsWith('//')) {
|
||||
// Add an empty authority if we don't already have one
|
||||
path = '//' + path;
|
||||
}
|
||||
return `${startQuote}${Schemas.vscodeWebviewResource}://${id}/file${path}${endQuote}`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the loading of resources inside of a webview.
|
||||
*/
|
||||
export class WebviewResourceRequestManager extends Disposable {
|
||||
|
||||
private readonly _webviewManagerService: IWebviewManagerService;
|
||||
|
||||
private _localResourceRoots: ReadonlyArray<URI>;
|
||||
private _portMappings: ReadonlyArray<modes.IWebviewPortMapping>;
|
||||
|
||||
private _ready: Promise<void>;
|
||||
|
||||
constructor(
|
||||
private readonly id: string,
|
||||
private readonly extension: WebviewExtensionDescription | undefined,
|
||||
initialContentOptions: WebviewContentOptions,
|
||||
getWebContentsId: Promise<number | undefined>,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IMainProcessService mainProcessService: IMainProcessService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): init`);
|
||||
|
||||
this._webviewManagerService = createChannelSender<IWebviewManagerService>(mainProcessService.getChannel('webview'));
|
||||
|
||||
this._localResourceRoots = initialContentOptions.localResourceRoots || [];
|
||||
this._portMappings = initialContentOptions.portMapping || [];
|
||||
|
||||
const remoteAuthority = environmentService.configuration.remoteAuthority;
|
||||
const remoteConnectionData = remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null;
|
||||
|
||||
this._ready = getWebContentsId.then(async (webContentsId) => {
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): did-start-loading`);
|
||||
|
||||
await this._webviewManagerService.registerWebview(this.id, webContentsId, {
|
||||
extensionLocation: this.extension?.location.toJSON(),
|
||||
localResourceRoots: this._localResourceRoots.map(x => x.toJSON()),
|
||||
remoteConnectionData: remoteConnectionData,
|
||||
portMappings: this._portMappings,
|
||||
});
|
||||
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): did register`);
|
||||
});
|
||||
|
||||
if (remoteAuthority) {
|
||||
this._register(remoteAuthorityResolverService.onDidChangeConnectionData(() => {
|
||||
const update = this._webviewManagerService.updateWebviewMetadata(this.id, {
|
||||
remoteConnectionData: remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null,
|
||||
});
|
||||
this._ready = this._ready.then(() => update);
|
||||
}));
|
||||
}
|
||||
|
||||
this._register(toDisposable(() => this._webviewManagerService.unregisterWebview(this.id)));
|
||||
}
|
||||
|
||||
public update(options: WebviewContentOptions) {
|
||||
const localResourceRoots = options.localResourceRoots || [];
|
||||
const portMappings = options.portMapping || [];
|
||||
|
||||
if (!this.needsUpdate(localResourceRoots, portMappings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._localResourceRoots = localResourceRoots;
|
||||
this._portMappings = portMappings;
|
||||
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): will update`);
|
||||
|
||||
const update = this._webviewManagerService.updateWebviewMetadata(this.id, {
|
||||
localResourceRoots: localResourceRoots.map(x => x.toJSON()),
|
||||
portMappings: portMappings,
|
||||
}).then(() => {
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): did update`);
|
||||
});
|
||||
|
||||
this._ready = this._ready.then(() => update);
|
||||
}
|
||||
|
||||
private needsUpdate(
|
||||
localResourceRoots: readonly URI[],
|
||||
portMappings: readonly modes.IWebviewPortMapping[],
|
||||
): boolean {
|
||||
return !(
|
||||
equals(this._localResourceRoots, localResourceRoots, (a, b) => a.toString() === b.toString())
|
||||
&& equals(this._portMappings, portMappings, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort)
|
||||
);
|
||||
}
|
||||
|
||||
public ensureReady(): Promise<void> {
|
||||
return this._ready;
|
||||
}
|
||||
}
|
|
@ -5,22 +5,19 @@
|
|||
|
||||
import { FindInPageOptions, WebviewTag } from 'electron';
|
||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
|
||||
import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
|
||||
|
@ -29,97 +26,7 @@ import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/t
|
|||
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { WebviewFindDelegate, WebviewFindWidget } from '../browser/webviewFindWidget';
|
||||
|
||||
class WebviewResourceRequestManager extends Disposable {
|
||||
|
||||
private readonly _webviewManagerService: IWebviewManagerService;
|
||||
|
||||
private _localResourceRoots: ReadonlyArray<URI>;
|
||||
private _portMappings: ReadonlyArray<modes.IWebviewPortMapping>;
|
||||
|
||||
private _ready?: Promise<void>;
|
||||
|
||||
constructor(
|
||||
private readonly id: string,
|
||||
private readonly extension: WebviewExtensionDescription | undefined,
|
||||
webview: WebviewTag,
|
||||
initialContentOptions: WebviewContentOptions,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IMainProcessService mainProcessService: IMainProcessService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): init`);
|
||||
|
||||
this._webviewManagerService = createChannelSender<IWebviewManagerService>(mainProcessService.getChannel('webview'));
|
||||
|
||||
this._localResourceRoots = initialContentOptions.localResourceRoots || [];
|
||||
this._portMappings = initialContentOptions.portMapping || [];
|
||||
|
||||
const remoteAuthority = environmentService.configuration.remoteAuthority;
|
||||
const remoteConnectionData = remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null;
|
||||
|
||||
this._ready = new Promise(resolve => {
|
||||
this._register(addDisposableListener(webview!, 'did-start-loading', once(() => {
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): did-start-loading`);
|
||||
|
||||
const webContentsId = webview.getWebContentsId();
|
||||
|
||||
this._webviewManagerService.registerWebview(this.id, webContentsId, {
|
||||
extensionLocation: this.extension?.location.toJSON(),
|
||||
localResourceRoots: this._localResourceRoots.map(x => x.toJSON()),
|
||||
remoteConnectionData: remoteConnectionData,
|
||||
portMappings: this._portMappings,
|
||||
}).then(() => {
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): did register`);
|
||||
}).finally(() => resolve());
|
||||
})));
|
||||
});
|
||||
|
||||
if (remoteAuthority) {
|
||||
this._register(remoteAuthorityResolverService.onDidChangeConnectionData(() => {
|
||||
const update = this._webviewManagerService.updateWebviewMetadata(this.id, {
|
||||
remoteConnectionData: remoteAuthority ? remoteAuthorityResolverService.getConnectionData(remoteAuthority) : null,
|
||||
});
|
||||
this._ready = this._ready?.then(() => update);
|
||||
}));
|
||||
}
|
||||
|
||||
this._register(toDisposable(() => this._webviewManagerService.unregisterWebview(this.id)));
|
||||
}
|
||||
|
||||
public update(options: WebviewContentOptions) {
|
||||
const localResourceRoots = options.localResourceRoots || [];
|
||||
const portMappings = options.portMapping || [];
|
||||
|
||||
if (
|
||||
equals(this._localResourceRoots, localResourceRoots, (a, b) => a.toString() === b.toString())
|
||||
&& equals(this._portMappings, portMappings, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._localResourceRoots = localResourceRoots;
|
||||
this._portMappings = portMappings;
|
||||
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): will update`);
|
||||
|
||||
const update = this._webviewManagerService.updateWebviewMetadata(this.id, {
|
||||
localResourceRoots: localResourceRoots.map(x => x.toJSON()),
|
||||
portMappings: portMappings,
|
||||
}).then(() => {
|
||||
this._logService.debug(`WebviewResourceRequestManager(${this.id}): did update`);
|
||||
});
|
||||
|
||||
this._ready = this._ready?.then(() => update);
|
||||
}
|
||||
|
||||
async synchronize(): Promise<void> {
|
||||
return this._ready;
|
||||
}
|
||||
}
|
||||
import { WebviewResourceRequestManager, rewriteVsCodeResourceUrls } from './resourceLoading';
|
||||
|
||||
class WebviewKeyboardHandler {
|
||||
|
||||
|
@ -222,7 +129,17 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
|
||||
this._myLogService.debug(`Webview(${this.id}): init`);
|
||||
|
||||
this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.element!, this.content.options));
|
||||
const webviewId = new Promise<number | undefined>((resolve, reject) => {
|
||||
const sub = this._register(addDisposableListener(this.element!, 'did-start-loading', once(() => {
|
||||
if (!this.element) {
|
||||
reject();
|
||||
throw new Error('No element');
|
||||
}
|
||||
resolve(this.element.getWebContentsId());
|
||||
sub.dispose();
|
||||
})));
|
||||
});
|
||||
this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options, webviewId));
|
||||
|
||||
this._register(addDisposableListener(this.element!, 'did-start-loading', once(() => {
|
||||
this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService, mainProcessService).add(this.element!));
|
||||
|
@ -289,7 +206,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
}
|
||||
|
||||
this.element!.preload = require.toUrl('./pre/electron-index.js');
|
||||
this.element!.src = 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%20role%3D%22document%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E';
|
||||
this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser/index.html`;
|
||||
}
|
||||
|
||||
protected createElement(options: WebviewOptions) {
|
||||
|
@ -301,6 +218,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
element.focus = () => {
|
||||
this.doFocus();
|
||||
};
|
||||
|
||||
element.setAttribute('partition', webviewPartitionId);
|
||||
element.setAttribute('webpreferences', 'contextIsolation=yes');
|
||||
element.className = `webview ${options.customClasses || ''}`;
|
||||
|
@ -332,24 +250,9 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
public set html(value: string) {
|
||||
this._myLogService.debug(`Webview(${this.id}): will set html`);
|
||||
|
||||
super.html = this.preprocessHtml(value);
|
||||
super.html = rewriteVsCodeResourceUrls(this.id, value);
|
||||
}
|
||||
|
||||
private preprocessHtml(value: string): string {
|
||||
return value
|
||||
.replace(/(["'])vscode-resource:(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (match, startQuote, _1, scheme, path, endQuote) => {
|
||||
if (scheme) {
|
||||
return `${startQuote}${Schemas.vscodeWebviewResource}://${this.id}/${scheme}${path}${endQuote}`;
|
||||
}
|
||||
if (!path.startsWith('//')) {
|
||||
// Add an empty authority if we don't already have one
|
||||
path = '//' + path;
|
||||
}
|
||||
return `${startQuote}${Schemas.vscodeWebviewResource}://${this.id}/file${path}${endQuote}`;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public mountTo(parent: HTMLElement) {
|
||||
if (!this.element) {
|
||||
return;
|
||||
|
@ -365,7 +268,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
|||
this._myLogService.debug(`Webview(${this.id}): will post message on '${channel}'`);
|
||||
|
||||
this._messagePromise = this._messagePromise
|
||||
.then(() => this._resourceRequestManager.synchronize())
|
||||
.then(() => this._resourceRequestManager.ensureReady())
|
||||
.then(() => {
|
||||
this._myLogService.debug(`Webview(${this.id}): did post message on '${channel}'`);
|
||||
return this.element?.send(channel, data);
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay';
|
||||
import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement';
|
||||
import { WebviewIconManager } from 'vs/workbench/contrib/webview/browser/webviewIconManager';
|
||||
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing';
|
||||
import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewIcons, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewIconManager } from 'vs/workbench/contrib/webview/browser/webviewIconManager';
|
||||
import { ElectronIframeWebview } from 'vs/workbench/contrib/webview/electron-browser/iframeWebviewElement';
|
||||
import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
|
||||
|
||||
export class ElectronWebviewService implements IWebviewService {
|
||||
|
@ -32,12 +32,8 @@ export class ElectronWebviewService implements IWebviewService {
|
|||
contentOptions: WebviewContentOptions,
|
||||
extension: WebviewExtensionDescription | undefined,
|
||||
): WebviewElement {
|
||||
const useExternalEndpoint = this._configService.getValue<string>('webview.experimental.useExternalEndpoint');
|
||||
if (useExternalEndpoint) {
|
||||
return this._instantiationService.createInstance(IFrameWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider);
|
||||
} else {
|
||||
return this._instantiationService.createInstance(ElectronWebviewBasedWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider);
|
||||
}
|
||||
const useIframes = this._configService.getValue<string>('webview.experimental.useIframes');
|
||||
return this._instantiationService.createInstance(useIframes ? ElectronIframeWebview : ElectronWebviewBasedWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider);
|
||||
}
|
||||
|
||||
createWebviewOverlay(
|
||||
|
|
Loading…
Reference in a new issue