Skip to content

Commit 8a58c3e

Browse files
committed
Try to disable menu keyboard shortcuts on macos when executing command inside of webview
Fixes #64724 Should prevent double dispatch of keyboard events in most cases
1 parent 23508cf commit 8a58c3e

File tree

1 file changed

+76
-27
lines changed

1 file changed

+76
-27
lines changed

src/vs/workbench/parts/webview/electron-browser/webviewElement.ts

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { WebviewFindWidget } from './webviewFindWidget';
1818
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
1919
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
2020
import { endsWith } from 'vs/base/common/strings';
21+
import { isMacintosh } from 'vs/base/common/platform';
2122

2223
export interface WebviewOptions {
2324
readonly allowScripts?: boolean;
@@ -144,6 +145,79 @@ class SvgBlocker extends Disposable {
144145
}
145146
}
146147

148+
class WebviewKeyboardHandler extends Disposable {
149+
constructor(
150+
private readonly _webview: Electron.WebviewTag,
151+
private readonly _keybindingService: IKeybindingService
152+
) {
153+
super();
154+
155+
if (this.shouldToggleMenuShortcutsEnablement) {
156+
this._register(addDisposableListener(this._webview, 'did-start-loading', () => {
157+
const contents = this.getWebContents();
158+
if (contents) {
159+
contents.on('before-input-event', (_event, input) => {
160+
contents.setIgnoreMenuShortcuts(input.control || input.meta);
161+
});
162+
}
163+
}));
164+
}
165+
166+
this._register(addDisposableListener(this._webview, 'ipc-message', (event) => {
167+
switch (event.channel) {
168+
case 'did-keydown':
169+
// Electron: workaround for https://github.com/electron/electron/issues/14258
170+
// We have to detect keyboard events in the <webview> and dispatch them to our
171+
// keybinding service because these events do not bubble to the parent window anymore.
172+
this.handleKeydown(event.args[0]);
173+
return;
174+
175+
case 'did-blur':
176+
if (this.shouldToggleMenuShortcutsEnablement) {
177+
const contents = this.getWebContents();
178+
if (contents) {
179+
contents.setIgnoreMenuShortcuts(false);
180+
}
181+
}
182+
return;
183+
}
184+
}));
185+
}
186+
187+
private get shouldToggleMenuShortcutsEnablement() {
188+
return isMacintosh;
189+
}
190+
191+
private getWebContents(): Electron.WebContents | undefined {
192+
const contents = this._webview.getWebContents();
193+
if (contents && !contents.isDestroyed()) {
194+
return contents;
195+
}
196+
return undefined;
197+
}
198+
199+
private handleKeydown(event: IKeydownEvent): void {
200+
// return;
201+
// Create a fake KeyboardEvent from the data provided
202+
const emulatedKeyboardEvent = new KeyboardEvent('keydown', {
203+
code: event.code,
204+
key: event.key,
205+
keyCode: event.keyCode,
206+
shiftKey: event.shiftKey,
207+
altKey: event.altKey,
208+
ctrlKey: event.ctrlKey,
209+
metaKey: event.metaKey,
210+
repeat: event.repeat
211+
} as KeyboardEvent);
212+
213+
// Dispatch through our keybinding service
214+
// Note: we set the <webview> as target of the event so that scoped context key
215+
// services function properly to enable commands like select all and find.
216+
this._keybindingService.dispatchEvent(new StandardKeyboardEvent(emulatedKeyboardEvent), this._webview);
217+
}
218+
}
219+
220+
147221
export class WebviewElement extends Disposable {
148222
private _webview: Electron.WebviewTag;
149223
private _ready: Promise<void>;
@@ -202,6 +276,8 @@ export class WebviewElement extends Disposable {
202276
const svgBlocker = this._register(new SvgBlocker(this._webview, this._options));
203277
svgBlocker.onDidBlockSvg(() => this.onDidBlockSvg());
204278

279+
this._register(new WebviewKeyboardHandler(this._webview, this._keybindingService));
280+
205281
this._register(addDisposableListener(this._webview, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) {
206282
console.log(`[Embedded Page] ${e.message}`);
207283
}));
@@ -253,13 +329,6 @@ export class WebviewElement extends Disposable {
253329
case 'did-blur':
254330
this.handleFocusChange(false);
255331
return;
256-
257-
case 'did-keydown':
258-
// Electron: workaround for https://github.com/electron/electron/issues/14258
259-
// We have to detect keyboard events in the <webview> and dispatch them to our
260-
// keybinding service because these events do not bubble to the parent window anymore.
261-
this.handleKeydown(event.args[0]);
262-
return;
263332
}
264333
}));
265334
this._register(addDisposableListener(this._webview, 'devtools-opened', () => {
@@ -368,26 +437,6 @@ export class WebviewElement extends Disposable {
368437
}
369438
}
370439

371-
private handleKeydown(event: IKeydownEvent): void {
372-
373-
// Create a fake KeyboardEvent from the data provided
374-
const emulatedKeyboardEvent = new KeyboardEvent('keydown', {
375-
code: event.code,
376-
key: event.key,
377-
keyCode: event.keyCode,
378-
shiftKey: event.shiftKey,
379-
altKey: event.altKey,
380-
ctrlKey: event.ctrlKey,
381-
metaKey: event.metaKey,
382-
repeat: event.repeat
383-
} as KeyboardEvent);
384-
385-
// Dispatch through our keybinding service
386-
// Note: we set the <webview> as target of the event so that scoped context key
387-
// services function properly to enable commands like select all and find.
388-
this._keybindingService.dispatchEvent(new StandardKeyboardEvent(emulatedKeyboardEvent), this._webview);
389-
}
390-
391440
public sendMessage(data: any): void {
392441
this._send('message', data);
393442
}

0 commit comments

Comments
 (0)