mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Switch entirely to iframe based webviews on desktop
Fixes #83188 Part of #92164 Removes our usage of the electron `<webview>` tag
This commit is contained in:
parent
d0b9f5f8cf
commit
2222f3cc1d
|
@ -65,7 +65,6 @@ const vscodeResources = [
|
||||||
'out-build/vs/workbench/contrib/debug/**/*.json',
|
'out-build/vs/workbench/contrib/debug/**/*.json',
|
||||||
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
|
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
|
||||||
'out-build/vs/workbench/contrib/webview/browser/pre/*.js',
|
'out-build/vs/workbench/contrib/webview/browser/pre/*.js',
|
||||||
'out-build/vs/workbench/contrib/webview/electron-browser/pre/*.js',
|
|
||||||
'out-build/vs/**/markdown.css',
|
'out-build/vs/**/markdown.css',
|
||||||
'out-build/vs/workbench/contrib/tasks/**/*.json',
|
'out-build/vs/workbench/contrib/tasks/**/*.json',
|
||||||
'out-build/vs/platform/files/**/*.exe',
|
'out-build/vs/platform/files/**/*.exe',
|
||||||
|
|
|
@ -40,10 +40,7 @@ export class WebviewProtocolProvider extends Disposable {
|
||||||
const uri = URI.parse(request.url);
|
const uri = URI.parse(request.url);
|
||||||
const entry = WebviewProtocolProvider.validWebviewFilePaths.get(uri.path);
|
const entry = WebviewProtocolProvider.validWebviewFilePaths.get(uri.path);
|
||||||
if (typeof entry === 'string') {
|
if (typeof entry === 'string') {
|
||||||
const relativeResourcePath = uri.path.startsWith('/electron-browser')
|
const relativeResourcePath = `vs/workbench/contrib/webview/browser/pre/${entry}`;
|
||||||
? `vs/workbench/contrib/webview/electron-browser/pre/${entry}`
|
|
||||||
: `vs/workbench/contrib/webview/browser/pre/${entry}`;
|
|
||||||
|
|
||||||
const url = FileAccess.asFileUri(relativeResourcePath, require);
|
const url = FileAccess.asFileUri(relativeResourcePath, require);
|
||||||
return callback(decodeURIComponent(url.fsPath));
|
return callback(decodeURIComponent(url.fsPath));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
// @ts-check
|
|
||||||
(function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const { ipcRenderer, contextBridge } = require('electron');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('../../browser/pre/main').WebviewHost & {isInDevelopmentMode: boolean}}
|
|
||||||
*/
|
|
||||||
const host = {
|
|
||||||
onElectron: true,
|
|
||||||
useParentPostMessage: true,
|
|
||||||
postMessage: (channel, data) => {
|
|
||||||
ipcRenderer.sendToHost(channel, data);
|
|
||||||
},
|
|
||||||
onMessage: (channel, handler) => {
|
|
||||||
ipcRenderer.on(channel, handler);
|
|
||||||
},
|
|
||||||
focusIframeOnCreate: true,
|
|
||||||
isInDevelopmentMode: false
|
|
||||||
};
|
|
||||||
|
|
||||||
host.onMessage('devtools-opened', () => {
|
|
||||||
host.isInDevelopmentMode = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', e => {
|
|
||||||
// Forward messages from the embedded iframe
|
|
||||||
window.onmessage = (/** @type {MessageEvent} */ event) => {
|
|
||||||
ipcRenderer.sendToHost(event.data.command, event.data.data);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('vscodeHost', host);
|
|
||||||
}());
|
|
|
@ -1,49 +0,0 @@
|
||||||
<!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">
|
|
||||||
<script async type="module">
|
|
||||||
import { createWebviewManager } from './main.js';
|
|
||||||
|
|
||||||
createWebviewManager({
|
|
||||||
...window.vscodeHost,
|
|
||||||
onIframeLoaded: (newFrame) => {
|
|
||||||
newFrame.contentWindow.onbeforeunload = () => {
|
|
||||||
if (window.vscodeHost.isInDevelopmentMode) { // Allow reloads while developing a webview
|
|
||||||
window.vscodeHost.postMessage('do-reload');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Block navigation when not in development mode
|
|
||||||
console.log('prevented webview navigation');
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Electron 4 eats mouseup events from inside webviews
|
|
||||||
// https://github.com/microsoft/vscode/issues/75090
|
|
||||||
// Try to fix this by rebroadcasting mouse moves and mouseups so that we can
|
|
||||||
// emulate these on the main window
|
|
||||||
let isMouseDown = false;
|
|
||||||
newFrame.contentWindow.addEventListener('mousedown', () => {
|
|
||||||
isMouseDown = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const tryDispatchSyntheticMouseEvent = (e) => {
|
|
||||||
if (!isMouseDown) {
|
|
||||||
window.vscodeHost.postMessage('synthetic-mouse-event', { type: e.type, screenX: e.screenX, screenY: e.screenY, clientX: e.clientX, clientY: e.clientY });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
newFrame.contentWindow.addEventListener('mouseup', e => {
|
|
||||||
tryDispatchSyntheticMouseEvent(e);
|
|
||||||
isMouseDown = false;
|
|
||||||
});
|
|
||||||
newFrame.contentWindow.addEventListener('mousemove', tryDispatchSyntheticMouseEvent);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,333 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { FindInPageOptions, WebviewTag } from 'electron';
|
|
||||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
|
||||||
import { once } from 'vs/base/common/functional';
|
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { FileAccess, Schemas } from 'vs/base/common/network';
|
|
||||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
|
||||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
|
||||||
import { IFileService } from 'vs/platform/files/common/files';
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
|
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
|
||||||
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
|
||||||
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
|
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|
||||||
import { webviewPartitionId } from 'vs/platform/webview/common/webviewManagerService';
|
|
||||||
import { BaseWebview, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement';
|
|
||||||
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing';
|
|
||||||
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
|
|
||||||
import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/webview/browser/webviewFindWidget';
|
|
||||||
import { WebviewIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager';
|
|
||||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
|
||||||
|
|
||||||
export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> implements Webview, WebviewFindDelegate {
|
|
||||||
private static _webviewKeyboardHandler: WebviewIgnoreMenuShortcutsManager | undefined;
|
|
||||||
|
|
||||||
public readonly checkImeCompletionState = false;
|
|
||||||
|
|
||||||
private static getWebviewKeyboardHandler(
|
|
||||||
configService: IConfigurationService,
|
|
||||||
mainProcessService: IMainProcessService,
|
|
||||||
) {
|
|
||||||
if (!this._webviewKeyboardHandler) {
|
|
||||||
this._webviewKeyboardHandler = new WebviewIgnoreMenuShortcutsManager(configService, mainProcessService);
|
|
||||||
}
|
|
||||||
return this._webviewKeyboardHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _webviewFindWidget: WebviewFindWidget | undefined;
|
|
||||||
private _findStarted: boolean = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
id: string,
|
|
||||||
options: WebviewOptions,
|
|
||||||
contentOptions: WebviewContentOptions,
|
|
||||||
extension: WebviewExtensionDescription | undefined,
|
|
||||||
private readonly _webviewThemeDataProvider: WebviewThemeDataProvider,
|
|
||||||
@IContextMenuService contextMenuService: IContextMenuService,
|
|
||||||
@ILogService private readonly _myLogService: ILogService,
|
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
|
||||||
@ITelemetryService telemetryService: ITelemetryService,
|
|
||||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
|
||||||
@IConfigurationService configurationService: IConfigurationService,
|
|
||||||
@IMainProcessService mainProcessService: IMainProcessService,
|
|
||||||
@IMenuService menuService: IMenuService,
|
|
||||||
@INotificationService notificationService: INotificationService,
|
|
||||||
@IFileService fileService: IFileService,
|
|
||||||
@ITunnelService tunnelService: ITunnelService,
|
|
||||||
@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
|
||||||
) {
|
|
||||||
super(id, options, contentOptions, extension, _webviewThemeDataProvider, {
|
|
||||||
contextMenuService,
|
|
||||||
notificationService,
|
|
||||||
logService: _myLogService,
|
|
||||||
telemetryService,
|
|
||||||
environmentService,
|
|
||||||
fileService,
|
|
||||||
menuService,
|
|
||||||
tunnelService,
|
|
||||||
remoteAuthorityResolverService
|
|
||||||
});
|
|
||||||
|
|
||||||
/* __GDPR__
|
|
||||||
"webview.createWebview" : {
|
|
||||||
"extension": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
|
||||||
"enableFindWidget": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
|
||||||
"webviewElementType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
telemetryService.publicLog('webview.createWebview', {
|
|
||||||
enableFindWidget: !!options.enableFindWidget,
|
|
||||||
extension: extension?.id.value,
|
|
||||||
webviewElementType: 'webview',
|
|
||||||
});
|
|
||||||
|
|
||||||
this._myLogService.debug(`Webview(${this.id}): init`);
|
|
||||||
|
|
||||||
this._register(addDisposableListener(this.element!, 'dom-ready', once(() => {
|
|
||||||
this._register(ElectronWebviewBasedWebview.getWebviewKeyboardHandler(configurationService, mainProcessService).add(this.element!));
|
|
||||||
})));
|
|
||||||
|
|
||||||
this._register(addDisposableListener(this.element!, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) {
|
|
||||||
console.log(`[Embedded Page] ${e.message}`);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._register(addDisposableListener(this.element!, 'dom-ready', () => {
|
|
||||||
this._myLogService.debug(`Webview(${this.id}): dom-ready`);
|
|
||||||
|
|
||||||
// Workaround for https://github.com/electron/electron/issues/14474
|
|
||||||
if (this.element && (this.isFocused || document.activeElement === this.element)) {
|
|
||||||
this.element.blur();
|
|
||||||
this.element.focus();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._register(addDisposableListener(this.element!, 'crashed', () => {
|
|
||||||
console.error('embedded page crashed');
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._register(this.on('synthetic-mouse-event', (rawEvent: any) => {
|
|
||||||
if (!this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const bounds = this.element.getBoundingClientRect();
|
|
||||||
try {
|
|
||||||
window.dispatchEvent(new MouseEvent(rawEvent.type, {
|
|
||||||
...rawEvent,
|
|
||||||
clientX: rawEvent.clientX + bounds.left,
|
|
||||||
clientY: rawEvent.clientY + bounds.top,
|
|
||||||
}));
|
|
||||||
return;
|
|
||||||
} catch {
|
|
||||||
// CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._register(this.on('did-set-content', () => {
|
|
||||||
this._myLogService.debug(`Webview(${this.id}): did-set-content`);
|
|
||||||
|
|
||||||
if (this.element) {
|
|
||||||
this.element.style.flex = '';
|
|
||||||
this.element.style.width = '100%';
|
|
||||||
this.element.style.height = '100%';
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._register(addDisposableListener(this.element!, 'devtools-opened', () => {
|
|
||||||
this._send('devtools-opened');
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (options.enableFindWidget) {
|
|
||||||
this._webviewFindWidget = this._register(instantiationService.createInstance(WebviewFindWidget, this));
|
|
||||||
|
|
||||||
this._register(addDisposableListener(this.element!, 'found-in-page', e => {
|
|
||||||
this._hasFindResult.fire(e.result.matches > 0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.styledFindWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We must ensure to put a `file:` URI as the preload attribute
|
|
||||||
// and not the `vscode-file` URI because preload scripts are loaded
|
|
||||||
// via node.js from the main side and only allow `file:` protocol
|
|
||||||
this.element!.preload = FileAccess.asFileUri('./pre/electron-index.js', require).toString(true);
|
|
||||||
this.element!.src = `${Schemas.vscodeWebview}://${this.id}/electron-browser-index.html?platform=electron&id=${this.id}&vscode-resource-base-authority=${encodeURIComponent(this.webviewRootResourceAuthority)}&swVersion=${this._expectedServiceWorkerVersion}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createElement(options: WebviewOptions) {
|
|
||||||
// Do not start loading the webview yet.
|
|
||||||
// Wait the end of the ctor when all listeners have been hooked up.
|
|
||||||
const element = document.createElement('webview');
|
|
||||||
|
|
||||||
element.focus = () => {
|
|
||||||
this.doFocus();
|
|
||||||
};
|
|
||||||
|
|
||||||
element.setAttribute('partition', webviewPartitionId);
|
|
||||||
element.setAttribute('webpreferences', 'contextIsolation=yes');
|
|
||||||
element.className = `webview ${options.customClasses || ''}`;
|
|
||||||
|
|
||||||
element.style.flex = '0 1';
|
|
||||||
element.style.width = '0';
|
|
||||||
element.style.height = '0';
|
|
||||||
element.style.outline = '0';
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected elementFocusImpl() {
|
|
||||||
this.element?.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override set contentOptions(options: WebviewContentOptions) {
|
|
||||||
this._myLogService.debug(`Webview(${this.id}): will set content options`);
|
|
||||||
super.contentOptions = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly extraContentOptions = {};
|
|
||||||
|
|
||||||
public mountTo(parent: HTMLElement) {
|
|
||||||
if (!this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._webviewFindWidget) {
|
|
||||||
parent.appendChild(this._webviewFindWidget.getDomNode()!);
|
|
||||||
}
|
|
||||||
parent.appendChild(this.element);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async doPostMessage(channel: string, data?: any): Promise<void> {
|
|
||||||
this._myLogService.debug(`Webview(${this.id}): did post message on '${channel}'`);
|
|
||||||
this.element?.send(channel, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override style(): void {
|
|
||||||
super.style();
|
|
||||||
this.styledFindWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
private styledFindWidget() {
|
|
||||||
this._webviewFindWidget?.updateTheme(this._webviewThemeDataProvider.getTheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly _hasFindResult = this._register(new Emitter<boolean>());
|
|
||||||
public readonly hasFindResult: Event<boolean> = this._hasFindResult.event;
|
|
||||||
|
|
||||||
private readonly _onDidStopFind = this._register(new Emitter<void>());
|
|
||||||
public readonly onDidStopFind: Event<void> = this._onDidStopFind.event;
|
|
||||||
|
|
||||||
public startFind(value: string, options?: FindInPageOptions) {
|
|
||||||
if (!value || !this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure options is defined without modifying the original
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
// FindNext must be false for a first request
|
|
||||||
const findOptions: FindInPageOptions = {
|
|
||||||
forward: options.forward,
|
|
||||||
findNext: true,
|
|
||||||
matchCase: options.matchCase
|
|
||||||
};
|
|
||||||
|
|
||||||
this._findStarted = true;
|
|
||||||
this.element.findInPage(value, findOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Webviews expose a stateful find API.
|
|
||||||
* Successive calls to find will move forward or backward through onFindResults
|
|
||||||
* depending on the supplied options.
|
|
||||||
*
|
|
||||||
* @param value The string to search for. Empty strings are ignored.
|
|
||||||
*/
|
|
||||||
public find(value: string, previous: boolean): void {
|
|
||||||
if (!this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Searching with an empty value will throw an exception
|
|
||||||
if (!value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = { findNext: false, forward: !previous };
|
|
||||||
if (!this._findStarted) {
|
|
||||||
this.startFind(value, options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.findInPage(value, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public stopFind(keepSelection?: boolean): void {
|
|
||||||
this._hasFindResult.fire(false);
|
|
||||||
if (!this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._findStarted = false;
|
|
||||||
this.element.stopFindInPage(keepSelection ? 'keepSelection' : 'clearSelection');
|
|
||||||
this._onDidStopFind.fire();
|
|
||||||
}
|
|
||||||
|
|
||||||
public showFind() {
|
|
||||||
this._webviewFindWidget?.reveal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public hideFind() {
|
|
||||||
this._webviewFindWidget?.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
public runFindAction(previous: boolean) {
|
|
||||||
this._webviewFindWidget?.find(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override selectAll() {
|
|
||||||
this.element?.selectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override copy() {
|
|
||||||
this.element?.copy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override paste() {
|
|
||||||
this.element?.paste();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override cut() {
|
|
||||||
this.element?.cut();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override undo() {
|
|
||||||
this.element?.undo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override redo() {
|
|
||||||
this.element?.redo();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override on<T = unknown>(channel: WebviewMessageChannels | string, handler: (data: T) => void): IDisposable {
|
|
||||||
if (!this.element) {
|
|
||||||
throw new Error('Cannot add event listener. No webview element found.');
|
|
||||||
}
|
|
||||||
return addDisposableListener(this.element, 'ipc-message', (event) => {
|
|
||||||
if (!this.element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.channel === channel && event.args && event.args.length) {
|
|
||||||
handler(event.args[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { WebviewTag } from 'electron';
|
|
||||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
|
||||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { isMacintosh } from 'vs/base/common/platform';
|
|
||||||
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
|
||||||
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
|
|
||||||
import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
|
|
||||||
import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement';
|
|
||||||
|
|
||||||
export class WebviewIgnoreMenuShortcutsManager {
|
|
||||||
|
|
||||||
private readonly _webviews = new Set<WebviewTag>();
|
|
||||||
private readonly _isUsingNativeTitleBars: boolean;
|
|
||||||
|
|
||||||
private readonly webviewMainService: IWebviewManagerService;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
configurationService: IConfigurationService,
|
|
||||||
mainProcessService: IMainProcessService,
|
|
||||||
) {
|
|
||||||
this._isUsingNativeTitleBars = configurationService.getValue<string>('window.titleBarStyle') === 'native';
|
|
||||||
|
|
||||||
this.webviewMainService = ProxyChannel.toService<IWebviewManagerService>(mainProcessService.getChannel('webview'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public add(webview: WebviewTag): IDisposable {
|
|
||||||
this._webviews.add(webview);
|
|
||||||
|
|
||||||
const disposables = new DisposableStore();
|
|
||||||
|
|
||||||
if (this.shouldToggleMenuShortcutsEnablement) {
|
|
||||||
this.setIgnoreMenuShortcutsForWebview(webview, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
disposables.add(addDisposableListener(webview, 'ipc-message', (event) => {
|
|
||||||
switch (event.channel) {
|
|
||||||
case WebviewMessageChannels.didFocus:
|
|
||||||
this.setIgnoreMenuShortcuts(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WebviewMessageChannels.didBlur:
|
|
||||||
this.setIgnoreMenuShortcuts(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return toDisposable(() => {
|
|
||||||
disposables.dispose();
|
|
||||||
this._webviews.delete(webview);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private get shouldToggleMenuShortcutsEnablement() {
|
|
||||||
return isMacintosh || this._isUsingNativeTitleBars;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setIgnoreMenuShortcuts(value: boolean) {
|
|
||||||
for (const webview of this._webviews) {
|
|
||||||
this.setIgnoreMenuShortcutsForWebview(webview, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setIgnoreMenuShortcutsForWebview(webview: WebviewTag, value: boolean) {
|
|
||||||
if (this.shouldToggleMenuShortcutsEnablement) {
|
|
||||||
this.webviewMainService.setIgnoreMenuShortcuts({ webContentsId: webview.getWebContentsId() }, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,8 +6,8 @@
|
||||||
import { registerAction2 } from 'vs/platform/actions/common/actions';
|
import { registerAction2 } from 'vs/platform/actions/common/actions';
|
||||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||||
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
|
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
|
||||||
import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands';
|
import * as webviewCommands from 'vs/workbench/contrib/webview/electron-sandbox/webviewCommands';
|
||||||
import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService';
|
import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-sandbox/webviewService';
|
||||||
|
|
||||||
registerSingleton(IWebviewService, ElectronWebviewService, true);
|
registerSingleton(IWebviewService, ElectronWebviewService, true);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { WebviewTag } from 'electron';
|
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { Action2 } from 'vs/platform/actions/common/actions';
|
import { Action2 } from 'vs/platform/actions/common/actions';
|
||||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
@ -24,15 +23,6 @@ export class OpenWebviewDeveloperToolsAction extends Action2 {
|
||||||
async run(accessor: ServicesAccessor): Promise<void> {
|
async run(accessor: ServicesAccessor): Promise<void> {
|
||||||
const nativeHostService = accessor.get(INativeHostService);
|
const nativeHostService = accessor.get(INativeHostService);
|
||||||
|
|
||||||
const webviewElements = document.querySelectorAll('webview.ready');
|
|
||||||
for (const element of webviewElements) {
|
|
||||||
try {
|
|
||||||
(element as WebviewTag).openDevTools();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const iframeWebviewElements = document.querySelectorAll('iframe.webview.ready');
|
const iframeWebviewElements = document.querySelectorAll('iframe.webview.ready');
|
||||||
if (iframeWebviewElements.length) {
|
if (iframeWebviewElements.length) {
|
||||||
console.info(nls.localize('iframeWebviewAlert', "Using standard dev tools to debug iframe based webview"));
|
console.info(nls.localize('iframeWebviewAlert', "Using standard dev tools to debug iframe based webview"));
|
|
@ -3,31 +3,20 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
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 { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay';
|
||||||
import { WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
import { WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||||
import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService';
|
import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService';
|
||||||
import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
|
|
||||||
import { ElectronIframeWebview } from 'vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement';
|
import { ElectronIframeWebview } from 'vs/workbench/contrib/webview/electron-sandbox/iframeWebviewElement';
|
||||||
|
|
||||||
export class ElectronWebviewService extends WebviewService {
|
export class ElectronWebviewService extends WebviewService {
|
||||||
|
|
||||||
constructor(
|
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
|
||||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
|
||||||
) {
|
|
||||||
super(instantiationService);
|
|
||||||
}
|
|
||||||
|
|
||||||
override createWebviewElement(
|
override createWebviewElement(
|
||||||
id: string,
|
id: string,
|
||||||
options: WebviewOptions,
|
options: WebviewOptions,
|
||||||
contentOptions: WebviewContentOptions,
|
contentOptions: WebviewContentOptions,
|
||||||
extension: WebviewExtensionDescription | undefined,
|
extension: WebviewExtensionDescription | undefined,
|
||||||
): WebviewElement {
|
): WebviewElement {
|
||||||
const useIframes = this._configService.getValue<string>('webview.experimental.useIframes') ?? !options.enableFindWidget;
|
const webview = this._instantiationService.createInstance(ElectronIframeWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider);
|
||||||
const webview = this._instantiationService.createInstance(useIframes ? ElectronIframeWebview : ElectronWebviewBasedWebview, id, options, contentOptions, extension, this._webviewThemeDataProvider);
|
|
||||||
this.registerNewWebview(webview);
|
this.registerNewWebview(webview);
|
||||||
return webview;
|
return webview;
|
||||||
}
|
}
|
|
@ -91,10 +91,6 @@ import 'vs/workbench/services/remote/electron-browser/tunnelServiceImpl';
|
||||||
|
|
||||||
//#region --- workbench contributions
|
//#region --- workbench contributions
|
||||||
|
|
||||||
// Webview
|
|
||||||
import 'vs/workbench/contrib/webview/electron-browser/webview.contribution';
|
|
||||||
|
|
||||||
|
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
//
|
//
|
||||||
// NOTE: Please do NOT register services here. Use `registerSingleton()`
|
// NOTE: Please do NOT register services here. Use `registerSingleton()`
|
||||||
|
|
|
@ -142,4 +142,7 @@ import 'vs/workbench/contrib/tasks/electron-sandbox/taskService';
|
||||||
// External terminal
|
// External terminal
|
||||||
import 'vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution';
|
import 'vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution';
|
||||||
|
|
||||||
|
// Webview
|
||||||
|
import 'vs/workbench/contrib/webview/electron-sandbox/webview.contribution';
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
Loading…
Reference in a new issue