提交 ffb8c08d 编写于 作者: M Matt Bierner

Enable IgnoreMenuShortcuts for iframe based webviews

For #109702

Iframe based webviews need to redispatch keypresses so that VS Code commands are supported. However, if this command is also in the native menu bars, this can end up triggering the command twice (once from the menubar and once from the webview's dispatch)

We already fixed this for `<webview>` based webview. This change also fixes the issue for iframe based webviews
上级 c9bebe2c
......@@ -11,6 +11,14 @@ import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMappi
export const IWebviewManagerService = createDecorator<IWebviewManagerService>('webviewManagerService');
export interface WebviewWebContentsId {
readonly webContentsId: number;
}
export interface WebviewWindowId {
readonly windowId: number;
}
export interface IWebviewManagerService {
_serviceBrand: unknown;
......@@ -20,7 +28,7 @@ export interface IWebviewManagerService {
didLoadResource(requestId: number, content: VSBuffer | undefined): void;
setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise<void>;
setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise<void>;
}
export interface RegisterWebviewMetadata {
......
......@@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { webContents } from 'electron';
import { WebContents, webContents } from 'electron';
import { VSBuffer } from 'vs/base/common/buffer';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { IRequestService } from 'vs/platform/request/common/request';
import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService';
import { IWebviewManagerService, RegisterWebviewMetadata, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService';
import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider';
import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
......@@ -26,7 +26,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
@IFileService fileService: IFileService,
@IRequestService requestService: IRequestService,
@ITunnelService tunnelService: ITunnelService,
@IWindowsMainService windowsMainService: IWindowsMainService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
) {
super();
this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService, windowsMainService));
......@@ -70,11 +70,24 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
});
}
public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise<void> {
const contents = webContents.fromId(webContentsId);
if (!contents) {
throw new Error(`Invalid webContentsId: ${webContentsId}`);
public async setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise<void> {
let contents: WebContents | undefined;
if (typeof (id as WebviewWindowId).windowId === 'number') {
const { windowId } = (id as WebviewWindowId);
const window = this.windowsMainService.getWindowById(windowId);
if (!window) {
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);
}
......
......@@ -8,96 +8,35 @@ import { addDisposableListener } from 'vs/base/browser/dom';
import { ThrottledDelayer } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { once } from 'vs/base/common/functional';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDisposable } from 'vs/base/common/lifecycle';
import { FileAccess, 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 { IConfigurationService } from 'vs/platform/configuration/common/configuration';
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 { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
import { IWebviewManagerService } 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 { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/webview/browser/webviewFindWidget';
import { WebviewResourceRequestManager, rewriteVsCodeResourceUrls } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading';
class WebviewKeyboardHandler {
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 = createChannelSender<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 'did-focus':
this.setIgnoreMenuShortcuts(true);
break;
case 'did-blur':
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(webview.getWebContentsId(), value);
}
}
}
import { WebviewIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-browser/webviewIgnoreMenuShortcutsManager';
import { rewriteVsCodeResourceUrls, WebviewResourceRequestManager } from 'vs/workbench/contrib/webview/electron-sandbox/resourceLoading';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> implements Webview, WebviewFindDelegate {
private static _webviewKeyboardHandler: WebviewKeyboardHandler | undefined;
private static _webviewKeyboardHandler: WebviewIgnoreMenuShortcutsManager | undefined;
private static getWebviewKeyboardHandler(
configService: IConfigurationService,
mainProcessService: IMainProcessService,
) {
if (!this._webviewKeyboardHandler) {
this._webviewKeyboardHandler = new WebviewKeyboardHandler(configService, mainProcessService);
this._webviewKeyboardHandler = new WebviewIgnoreMenuShortcutsManager(configService, mainProcessService);
}
return this._webviewKeyboardHandler;
}
......@@ -124,6 +63,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
@IConfigurationService configurationService: IConfigurationService,
@IMainProcessService mainProcessService: IMainProcessService,
@INotificationService noficationService: INotificationService,
@INativeHostService nativeHostService: INativeHostService,
) {
super(id, options, contentOptions, extension, _webviewThemeDataProvider, noficationService, _myLogService, telemetryService, environmentService);
......
/*---------------------------------------------------------------------------------------------
* 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 { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
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 = createChannelSender<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,18 +6,23 @@
import { ThrottledDelayer } from 'vs/base/common/async';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
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 { ILogService } from 'vs/platform/log/common/log';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
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 { IRequestService } from 'vs/platform/request/common/request';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/baseWebviewElement';
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-sandbox/resourceLoading';
import { WindowIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
/**
......@@ -31,6 +36,8 @@ export class ElectronIframeWebview extends IFrameWebview {
private readonly _focusDelayer = this._register(new ThrottledDelayer(10));
private _elementFocusImpl!: (options?: FocusOptions | undefined) => void;
private readonly _webviewKeyboardHandler: WindowIgnoreMenuShortcutsManager;
constructor(
id: string,
options: WebviewOptions,
......@@ -45,12 +52,25 @@ export class ElectronIframeWebview extends IFrameWebview {
@IRemoteAuthorityResolverService _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@ILogService logService: ILogService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@IMainProcessService mainProcessService: IMainProcessService,
@INotificationService noficationService: INotificationService,
@INativeHostService nativeHostService: INativeHostService,
) {
super(id, options, contentOptions, extension, webviewThemeDataProvider,
noficationService, tunnelService, fileService, requestService, telemetryService, environmentService, _remoteAuthorityResolverService, logService);
this._resourceRequestManager = this._register(instantiationService.createInstance(WebviewResourceRequestManager, id, extension, this.content.options));
this._webviewKeyboardHandler = new WindowIgnoreMenuShortcutsManager(configurationService, mainProcessService, nativeHostService);
this._register(this.on(WebviewMessageChannels.didFocus, () => {
this._webviewKeyboardHandler.didFocus();
}));
this._register(this.on(WebviewMessageChannels.didBlur, () => {
this._webviewKeyboardHandler.didBlur();
}));
}
protected createElement(options: WebviewOptions, contentOptions: WebviewContentOptions) {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isMacintosh } from 'vs/base/common/platform';
import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService';
export class WindowIgnoreMenuShortcutsManager {
private readonly _isUsingNativeTitleBars: boolean;
private readonly webviewMainService: IWebviewManagerService;
constructor(
configurationService: IConfigurationService,
mainProcessService: IMainProcessService,
private readonly nativeHostService: INativeHostService
) {
this._isUsingNativeTitleBars = configurationService.getValue<string>('window.titleBarStyle') === 'native';
this.webviewMainService = createChannelSender<IWebviewManagerService>(mainProcessService.getChannel('webview'));
}
public didFocus(): void {
this.setIgnoreMenuShortcuts(true);
}
public didBlur(): void {
this.setIgnoreMenuShortcuts(false);
}
private get shouldToggleMenuShortcutsEnablement() {
return isMacintosh || this._isUsingNativeTitleBars;
}
protected setIgnoreMenuShortcuts(value: boolean) {
if (this.shouldToggleMenuShortcutsEnablement) {
this.webviewMainService.setIgnoreMenuShortcuts({ windowId: this.nativeHostService.windowId }, value);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册