mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-17 15:24:40 +01:00
Browser: context menus (#299013)
* Browser: context menus * feedback * feedback * auxiliary fix
This commit is contained in:
@@ -7,6 +7,29 @@ import { Event } from '../../../base/common/event.js';
|
||||
import { VSBuffer } from '../../../base/common/buffer.js';
|
||||
import { URI } from '../../../base/common/uri.js';
|
||||
|
||||
const commandPrefix = 'workbench.action.browser';
|
||||
export enum BrowserViewCommandId {
|
||||
Open = `${commandPrefix}.open`,
|
||||
NewTab = `${commandPrefix}.newTab`,
|
||||
GoBack = `${commandPrefix}.goBack`,
|
||||
GoForward = `${commandPrefix}.goForward`,
|
||||
Reload = `${commandPrefix}.reload`,
|
||||
HardReload = `${commandPrefix}.hardReload`,
|
||||
FocusUrlInput = `${commandPrefix}.focusUrlInput`,
|
||||
AddElementToChat = `${commandPrefix}.addElementToChat`,
|
||||
AddConsoleLogsToChat = `${commandPrefix}.addConsoleLogsToChat`,
|
||||
ToggleDevTools = `${commandPrefix}.toggleDevTools`,
|
||||
OpenExternal = `${commandPrefix}.openExternal`,
|
||||
ClearGlobalStorage = `${commandPrefix}.clearGlobalStorage`,
|
||||
ClearWorkspaceStorage = `${commandPrefix}.clearWorkspaceStorage`,
|
||||
ClearEphemeralStorage = `${commandPrefix}.clearEphemeralStorage`,
|
||||
OpenSettings = `${commandPrefix}.openSettings`,
|
||||
ShowFind = `${commandPrefix}.showFind`,
|
||||
HideFind = `${commandPrefix}.hideFind`,
|
||||
FindNext = `${commandPrefix}.findNext`,
|
||||
FindPrevious = `${commandPrefix}.findPrevious`,
|
||||
}
|
||||
|
||||
export interface IBrowserViewBounds {
|
||||
windowId: number;
|
||||
x: number;
|
||||
@@ -287,4 +310,10 @@ export interface IBrowserViewService {
|
||||
* @param id The browser view identifier
|
||||
*/
|
||||
clearStorage(id: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Update the keybinding accelerators used in browser view context menus.
|
||||
* @param keybindings A map of command ID to accelerator label
|
||||
*/
|
||||
updateKeybindings(keybindings: { [commandId: string]: string }): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -11,15 +11,16 @@ import { VSBuffer } from '../../../base/common/buffer.js';
|
||||
import { IBrowserViewBounds, IBrowserViewDevToolsStateEvent, IBrowserViewFocusEvent, IBrowserViewKeyDownEvent, IBrowserViewState, IBrowserViewNavigationEvent, IBrowserViewLoadingEvent, IBrowserViewLoadError, IBrowserViewTitleChangeEvent, IBrowserViewFaviconChangeEvent, IBrowserViewNewPageRequest, IBrowserViewCaptureScreenshotOptions, IBrowserViewFindInPageOptions, IBrowserViewFindInPageResult, IBrowserViewVisibilityEvent, BrowserNewPageLocation, browserViewIsolatedWorldId } from '../common/browserView.js';
|
||||
import { EVENT_KEY_CODE_MAP, KeyCode, KeyMod, SCAN_CODE_STR_TO_EVENT_KEY_CODE } from '../../../base/common/keyCodes.js';
|
||||
import { IWindowsMainService } from '../../windows/electron-main/windows.js';
|
||||
import { IBaseWindow, ICodeWindow } from '../../window/electron-main/window.js';
|
||||
import { ICodeWindow } from '../../window/electron-main/window.js';
|
||||
import { IAuxiliaryWindowsMainService } from '../../auxiliaryWindow/electron-main/auxiliaryWindows.js';
|
||||
import { IAuxiliaryWindow } from '../../auxiliaryWindow/electron-main/auxiliaryWindow.js';
|
||||
import { isMacintosh } from '../../../base/common/platform.js';
|
||||
import { BrowserViewUri } from '../common/browserViewUri.js';
|
||||
import { BrowserViewDebugger } from './browserViewDebugger.js';
|
||||
import { ILogService } from '../../log/common/log.js';
|
||||
import { ICDPTarget, ICDPConnection, CDPTargetInfo } from '../common/cdp/types.js';
|
||||
import { BrowserSession } from './browserSession.js';
|
||||
import { IAuxiliaryWindow } from '../../auxiliaryWindow/electron-main/auxiliaryWindow.js';
|
||||
import { hasKey } from '../../../base/common/types.js';
|
||||
|
||||
/** Key combinations that are used in system-level shortcuts. */
|
||||
const nativeShortcuts = new Set([
|
||||
@@ -47,7 +48,7 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
private _lastUserGestureTimestamp: number = -Infinity;
|
||||
|
||||
private _debugger: BrowserViewDebugger;
|
||||
private _window: IBaseWindow | undefined;
|
||||
private _window: ICodeWindow | IAuxiliaryWindow | undefined;
|
||||
private _isSendingKeyEvent = false;
|
||||
private _isDisposed = false;
|
||||
|
||||
@@ -88,6 +89,7 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
public readonly id: string,
|
||||
public readonly session: BrowserSession,
|
||||
createChildView: (options?: Electron.WebContentsViewConstructorOptions) => BrowserView,
|
||||
openContextMenu: (view: BrowserView, params: Electron.ContextMenuParams) => void,
|
||||
options: Electron.WebContentsViewConstructorOptions | undefined,
|
||||
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
|
||||
@IAuxiliaryWindowsMainService private readonly auxiliaryWindowsMainService: IAuxiliaryWindowsMainService,
|
||||
@@ -150,6 +152,10 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
};
|
||||
});
|
||||
|
||||
this._view.webContents.on('context-menu', (_event, params) => {
|
||||
openContextMenu(this, params);
|
||||
});
|
||||
|
||||
this._view.webContents.on('destroyed', () => {
|
||||
this.dispose();
|
||||
});
|
||||
@@ -376,7 +382,7 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
*/
|
||||
layout(bounds: IBrowserViewBounds): void {
|
||||
if (this._window?.win?.id !== bounds.windowId) {
|
||||
const newWindow = this.windowById(bounds.windowId);
|
||||
const newWindow = this._windowById(bounds.windowId);
|
||||
if (newWindow) {
|
||||
this._window?.win?.contentView.removeChildView(this._view);
|
||||
this._window = newWindow;
|
||||
@@ -575,6 +581,22 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
return this._view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hosting Electron window for this view, if any.
|
||||
* This can be an auxiliary window, depending on where the view is currently hosted.
|
||||
*/
|
||||
getElectronWindow(): Electron.BrowserWindow | undefined {
|
||||
return this._window?.win ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main code window hosting this browser view, if any. This is used for routing commands from the browser view to the correct window.
|
||||
* If the browser view is hosted in an auxiliary window, this will return the parent code window of that auxiliary window.
|
||||
*/
|
||||
getTopCodeWindow(): ICodeWindow | undefined {
|
||||
return this._window && hasKey(this._window, { parentId: true }) ? this._codeWindowById(this._window.parentId) : undefined;
|
||||
}
|
||||
|
||||
// ============ ICDPTarget implementation ============
|
||||
|
||||
/**
|
||||
@@ -662,11 +684,11 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
return true;
|
||||
}
|
||||
|
||||
private windowById(windowId: number | undefined): ICodeWindow | IAuxiliaryWindow | undefined {
|
||||
return this.codeWindowById(windowId) ?? this.auxiliaryWindowById(windowId);
|
||||
private _windowById(windowId: number | undefined): ICodeWindow | IAuxiliaryWindow | undefined {
|
||||
return this._codeWindowById(windowId) ?? this._auxiliaryWindowById(windowId);
|
||||
}
|
||||
|
||||
private codeWindowById(windowId: number | undefined): ICodeWindow | undefined {
|
||||
private _codeWindowById(windowId: number | undefined): ICodeWindow | undefined {
|
||||
if (typeof windowId !== 'number') {
|
||||
return undefined;
|
||||
}
|
||||
@@ -674,7 +696,7 @@ export class BrowserView extends Disposable implements ICDPTarget {
|
||||
return this.windowsMainService.getWindowById(windowId);
|
||||
}
|
||||
|
||||
private auxiliaryWindowById(windowId: number | undefined): IAuxiliaryWindow | undefined {
|
||||
private _auxiliaryWindowById(windowId: number | undefined): IAuxiliaryWindow | undefined {
|
||||
if (typeof windowId !== 'number') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { Disposable, DisposableMap } from '../../../base/common/lifecycle.js';
|
||||
import { VSBuffer } from '../../../base/common/buffer.js';
|
||||
import { IBrowserViewBounds, IBrowserViewKeyDownEvent, IBrowserViewState, IBrowserViewService, BrowserViewStorageScope, IBrowserViewCaptureScreenshotOptions, IBrowserViewFindInPageOptions } from '../common/browserView.js';
|
||||
import { IBrowserViewBounds, IBrowserViewKeyDownEvent, IBrowserViewState, IBrowserViewService, BrowserViewStorageScope, IBrowserViewCaptureScreenshotOptions, IBrowserViewFindInPageOptions, BrowserViewCommandId } from '../common/browserView.js';
|
||||
import { clipboard, Menu, MenuItem } from 'electron';
|
||||
import { ICDPTarget, CDPBrowserVersion, CDPWindowBounds, CDPTargetInfo, ICDPConnection, ICDPBrowserTarget } from '../common/cdp/types.js';
|
||||
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
|
||||
import { createDecorator, IInstantiationService } from '../../instantiation/common/instantiation.js';
|
||||
@@ -17,9 +18,13 @@ import { IWindowsMainService } from '../../windows/electron-main/windows.js';
|
||||
import { BrowserSession } from './browserSession.js';
|
||||
import { IProductService } from '../../product/common/productService.js';
|
||||
import { CDPBrowserProxy } from '../common/cdp/proxy.js';
|
||||
import { logBrowserOpen } from '../common/browserViewTelemetry.js';
|
||||
import { IntegratedBrowserOpenSource, logBrowserOpen } from '../common/browserViewTelemetry.js';
|
||||
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
|
||||
import { CancellationToken } from '../../../base/common/cancellation.js';
|
||||
import { localize } from '../../../nls.js';
|
||||
import { INativeHostMainService } from '../../native/electron-main/nativeHostMainService.js';
|
||||
import { ITextEditorOptions } from '../../editor/common/editor.js';
|
||||
import { htmlAttributeEncodeValue } from '../../../base/common/strings.js';
|
||||
|
||||
export const IBrowserViewMainService = createDecorator<IBrowserViewMainService>('browserViewMainService');
|
||||
|
||||
@@ -41,6 +46,7 @@ export class BrowserViewMainService extends Disposable implements IBrowserViewMa
|
||||
}
|
||||
|
||||
private readonly browserViews = this._register(new DisposableMap<string, BrowserView>());
|
||||
private _keybindings: { [commandId: string]: string } = Object.create(null);
|
||||
|
||||
// ICDPBrowserTarget events
|
||||
private readonly _onTargetCreated = this._register(new Emitter<BrowserView>());
|
||||
@@ -54,38 +60,12 @@ export class BrowserViewMainService extends Disposable implements IBrowserViewMa
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a browser view backed by the given {@link BrowserSession}.
|
||||
*/
|
||||
private createBrowserView(id: string, browserSession: BrowserSession, options?: Electron.WebContentsViewConstructorOptions): BrowserView {
|
||||
if (this.browserViews.has(id)) {
|
||||
throw new Error(`Browser view with id ${id} already exists`);
|
||||
}
|
||||
|
||||
const view = this.instantiationService.createInstance(
|
||||
BrowserView,
|
||||
id,
|
||||
browserSession,
|
||||
// Recursive factory for nested windows (child views share the same session)
|
||||
(childOptions) => this.createBrowserView(generateUuid(), browserSession, childOptions),
|
||||
options
|
||||
);
|
||||
this.browserViews.set(id, view);
|
||||
|
||||
this._onTargetCreated.fire(view);
|
||||
Event.once(view.onDidClose)(() => {
|
||||
this._onTargetDestroyed.fire(view);
|
||||
this.browserViews.deleteAndDispose(id);
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
async getOrCreateBrowserView(id: string, scope: BrowserViewStorageScope, workspaceId?: string): Promise<IBrowserViewState> {
|
||||
if (this.browserViews.has(id)) {
|
||||
// Note: scope will be ignored if the view already exists.
|
||||
@@ -160,26 +140,14 @@ export class BrowserViewMainService extends Disposable implements IBrowserViewMa
|
||||
}
|
||||
|
||||
async createTarget(url: string, browserContextId?: string, windowId?: number): Promise<ICDPTarget> {
|
||||
const targetId = generateUuid();
|
||||
const browserSession = browserContextId && BrowserSession.get(browserContextId) || BrowserSession.getOrCreateEphemeral(targetId);
|
||||
const browserSession = browserContextId ? BrowserSession.get(browserContextId) : undefined;
|
||||
|
||||
// Create the browser view (fires onTargetCreated)
|
||||
const view = this.createBrowserView(targetId, browserSession);
|
||||
|
||||
logBrowserOpen(this.telemetryService, 'cdpCreated');
|
||||
|
||||
const window = windowId !== undefined ? this.windowsMainService.getWindowById(windowId) : this.windowsMainService.getFocusedWindow();
|
||||
if (!window) {
|
||||
throw new Error(`Window ${windowId} not found`);
|
||||
}
|
||||
|
||||
// Request the workbench to open the editor
|
||||
window.sendWhenReady('vscode:runAction', CancellationToken.None, {
|
||||
id: '_workbench.open',
|
||||
args: [BrowserViewUri.forUrl(url, targetId), [undefined, { preserveFocus: true }], undefined]
|
||||
return this.openNew(url, {
|
||||
session: browserSession,
|
||||
windowId,
|
||||
editorOptions: { preserveFocus: true },
|
||||
source: 'cdpCreated'
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
async activateTarget(target: ICDPTarget): Promise<void> {
|
||||
@@ -372,4 +340,182 @@ export class BrowserViewMainService extends Disposable implements IBrowserViewMa
|
||||
);
|
||||
await browserSession.electronSession.clearData();
|
||||
}
|
||||
|
||||
async updateKeybindings(keybindings: { [commandId: string]: string }): Promise<void> {
|
||||
this._keybindings = keybindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a browser view backed by the given {@link BrowserSession}.
|
||||
*/
|
||||
private createBrowserView(id: string, browserSession: BrowserSession, options?: Electron.WebContentsViewConstructorOptions): BrowserView {
|
||||
if (this.browserViews.has(id)) {
|
||||
throw new Error(`Browser view with id ${id} already exists`);
|
||||
}
|
||||
|
||||
const view = this.instantiationService.createInstance(
|
||||
BrowserView,
|
||||
id,
|
||||
browserSession,
|
||||
// Recursive factory for nested windows (child views share the same session)
|
||||
(childOptions) => this.createBrowserView(generateUuid(), browserSession, childOptions),
|
||||
(v, params) => this.showContextMenu(v, params),
|
||||
options
|
||||
);
|
||||
this.browserViews.set(id, view);
|
||||
|
||||
this._onTargetCreated.fire(view);
|
||||
Event.once(view.onDidClose)(() => {
|
||||
this._onTargetDestroyed.fire(view);
|
||||
this.browserViews.deleteAndDispose(id);
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private async openNew(
|
||||
url: string,
|
||||
{
|
||||
session,
|
||||
windowId,
|
||||
editorOptions,
|
||||
source
|
||||
}: {
|
||||
session: BrowserSession | undefined;
|
||||
windowId: number | undefined;
|
||||
editorOptions: ITextEditorOptions;
|
||||
source: IntegratedBrowserOpenSource;
|
||||
}
|
||||
): Promise<ICDPTarget> {
|
||||
const targetId = generateUuid();
|
||||
const view = this.createBrowserView(targetId, session || BrowserSession.getOrCreateEphemeral(targetId));
|
||||
|
||||
const window = windowId !== undefined ? this.windowsMainService.getWindowById(windowId) : this.windowsMainService.getFocusedWindow();
|
||||
if (!window) {
|
||||
throw new Error(`Window ${windowId} not found`);
|
||||
}
|
||||
|
||||
|
||||
logBrowserOpen(this.telemetryService, source);
|
||||
|
||||
// Request the workbench to open the editor
|
||||
window.sendWhenReady('vscode:runAction', CancellationToken.None, {
|
||||
id: '_workbench.open',
|
||||
args: [BrowserViewUri.forUrl(url, targetId), [undefined, editorOptions], undefined]
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private showContextMenu(view: BrowserView, params: Electron.ContextMenuParams): void {
|
||||
const win = view.getElectronWindow();
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
const webContents = view.webContents;
|
||||
if (webContents.isDestroyed()) {
|
||||
return;
|
||||
}
|
||||
const menu = new Menu();
|
||||
|
||||
if (params.linkURL) {
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.openLinkInNewTab', 'Open Link in New Tab'),
|
||||
click: () => {
|
||||
void this.openNew(params.linkURL, {
|
||||
session: view.session,
|
||||
windowId: view.getTopCodeWindow()?.id,
|
||||
editorOptions: { preserveFocus: true, inactive: true },
|
||||
source: 'browserLinkBackground'
|
||||
});
|
||||
}
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.openLinkInExternalBrowser', 'Open Link in External Browser'),
|
||||
click: () => { void this.nativeHostMainService.openExternal(undefined, params.linkURL); }
|
||||
}));
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.copyLink', 'Copy Link'),
|
||||
click: () => {
|
||||
clipboard.write({
|
||||
text: params.linkURL,
|
||||
html: `<a href="${encodeURI(params.linkURL)}">${htmlAttributeEncodeValue(params.linkText || params.linkURL)}</a>`
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (params.hasImageContents && params.srcURL) {
|
||||
if (menu.items.length > 0) {
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
}
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.openImageInNewTab', 'Open Image in New Tab'),
|
||||
click: () => {
|
||||
void this.openNew(params.srcURL!, {
|
||||
session: view.session,
|
||||
windowId: view.getTopCodeWindow()?.id,
|
||||
editorOptions: { preserveFocus: true, inactive: true },
|
||||
source: 'browserLinkBackground'
|
||||
});
|
||||
}
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.copyImage', 'Copy Image'),
|
||||
click: () => { view.webContents.copyImageAt(params.x, params.y); }
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.copyImageUrl', 'Copy Image URL'),
|
||||
click: () => { clipboard.writeText(params.srcURL!); }
|
||||
}));
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
menu.append(new MenuItem({ role: 'cut', enabled: params.editFlags.canCut }));
|
||||
menu.append(new MenuItem({ role: 'copy', enabled: params.editFlags.canCopy }));
|
||||
menu.append(new MenuItem({ role: 'paste', enabled: params.editFlags.canPaste }));
|
||||
menu.append(new MenuItem({ role: 'pasteAndMatchStyle', enabled: params.editFlags.canPaste }));
|
||||
menu.append(new MenuItem({ role: 'selectAll', enabled: params.editFlags.canSelectAll }));
|
||||
} else if (params.selectionText) {
|
||||
menu.append(new MenuItem({ role: 'copy' }));
|
||||
}
|
||||
|
||||
// Add navigation items as defaults
|
||||
if (menu.items.length === 0) {
|
||||
if (webContents.navigationHistory.canGoBack()) {
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.back', 'Back'),
|
||||
accelerator: this._keybindings[BrowserViewCommandId.GoBack],
|
||||
click: () => webContents.navigationHistory.goBack()
|
||||
}));
|
||||
}
|
||||
if (webContents.navigationHistory.canGoForward()) {
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.forward', 'Forward'),
|
||||
accelerator: this._keybindings[BrowserViewCommandId.GoForward],
|
||||
click: () => webContents.navigationHistory.goForward()
|
||||
}));
|
||||
}
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.reload', 'Reload'),
|
||||
accelerator: this._keybindings[BrowserViewCommandId.Reload],
|
||||
click: () => webContents.reload()
|
||||
}));
|
||||
}
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
menu.append(new MenuItem({
|
||||
label: localize('browser.contextMenu.inspect', 'Inspect'),
|
||||
click: () => webContents.inspectElement(params.x, params.y)
|
||||
}));
|
||||
|
||||
const viewBounds = view.getWebContentsView().getBounds();
|
||||
menu.popup({
|
||||
window: win,
|
||||
x: viewBounds.x + params.x,
|
||||
y: viewBounds.y + params.y,
|
||||
sourceType: params.menuSourceType
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi
|
||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||
import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js';
|
||||
import { IBrowserViewModel } from '../common/browserView.js';
|
||||
import { BrowserViewCommandId } from '../../../../platform/browserView/common/browserView.js';
|
||||
import { localize } from '../../../../nls.js';
|
||||
import { DisposableStore } from '../../../../base/common/lifecycle.js';
|
||||
|
||||
@@ -45,9 +46,9 @@ export class BrowserFindWidget extends SimpleFindWidget {
|
||||
showResultCount: true,
|
||||
enableSash: true,
|
||||
initialWidth: 350,
|
||||
previousMatchActionId: 'workbench.action.browser.findPrevious',
|
||||
nextMatchActionId: 'workbench.action.browser.findNext',
|
||||
closeWidgetActionId: 'workbench.action.browser.hideFind'
|
||||
previousMatchActionId: BrowserViewCommandId.FindPrevious,
|
||||
nextMatchActionId: BrowserViewCommandId.FindNext,
|
||||
closeWidgetActionId: BrowserViewCommandId.HideFind
|
||||
}, contextViewService, contextKeyService, hoverService, keybindingService, configurationService, accessibilityService);
|
||||
|
||||
this._findWidgetVisible = CONTEXT_BROWSER_FIND_WIDGET_VISIBLE.bindTo(contextKeyService);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Codicon } from '../../../../base/common/codicons.js';
|
||||
import { BrowserEditor, CONTEXT_BROWSER_CAN_GO_BACK, CONTEXT_BROWSER_CAN_GO_FORWARD, CONTEXT_BROWSER_DEVTOOLS_OPEN, CONTEXT_BROWSER_FOCUSED, CONTEXT_BROWSER_HAS_ERROR, CONTEXT_BROWSER_HAS_URL, CONTEXT_BROWSER_STORAGE_SCOPE, CONTEXT_BROWSER_ELEMENT_SELECTION_ACTIVE, CONTEXT_BROWSER_FIND_WIDGET_FOCUSED, CONTEXT_BROWSER_FIND_WIDGET_VISIBLE } from './browserEditor.js';
|
||||
import { BrowserViewUri } from '../../../../platform/browserView/common/browserViewUri.js';
|
||||
import { IBrowserViewWorkbenchService } from '../common/browserView.js';
|
||||
import { BrowserViewStorageScope } from '../../../../platform/browserView/common/browserView.js';
|
||||
import { BrowserViewCommandId, BrowserViewStorageScope } from '../../../../platform/browserView/common/browserView.js';
|
||||
import { ChatContextKeys } from '../../chat/common/actions/chatContextKeys.js';
|
||||
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
|
||||
import { IPreferencesService } from '../../../services/preferences/common/preferences.js';
|
||||
@@ -37,7 +37,7 @@ interface IOpenBrowserOptions {
|
||||
class OpenIntegratedBrowserAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.browser.open',
|
||||
id: BrowserViewCommandId.Open,
|
||||
title: localize2('browser.openAction', "Open Integrated Browser"),
|
||||
category: BrowserCategory,
|
||||
f1: true
|
||||
@@ -67,7 +67,7 @@ class OpenIntegratedBrowserAction extends Action2 {
|
||||
class NewTabAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.browser.newTab',
|
||||
id: BrowserViewCommandId.NewTab,
|
||||
title: localize2('browser.newTabAction', "New Tab"),
|
||||
category: BrowserCategory,
|
||||
f1: true,
|
||||
@@ -97,7 +97,7 @@ class NewTabAction extends Action2 {
|
||||
}
|
||||
|
||||
class GoBackAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.goBack';
|
||||
static readonly ID = BrowserViewCommandId.GoBack;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -129,7 +129,7 @@ class GoBackAction extends Action2 {
|
||||
}
|
||||
|
||||
class GoForwardAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.goForward';
|
||||
static readonly ID = BrowserViewCommandId.GoForward;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -161,7 +161,7 @@ class GoForwardAction extends Action2 {
|
||||
}
|
||||
|
||||
class ReloadAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.reload';
|
||||
static readonly ID = BrowserViewCommandId.Reload;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -199,7 +199,7 @@ class ReloadAction extends Action2 {
|
||||
}
|
||||
|
||||
class HardReloadAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.hardReload';
|
||||
static readonly ID = BrowserViewCommandId.HardReload;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -227,7 +227,7 @@ class HardReloadAction extends Action2 {
|
||||
}
|
||||
|
||||
class FocusUrlInputAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.focusUrlInput';
|
||||
static readonly ID = BrowserViewCommandId.FocusUrlInput;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -251,7 +251,7 @@ class FocusUrlInputAction extends Action2 {
|
||||
}
|
||||
|
||||
class AddElementToChatAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.addElementToChat';
|
||||
static readonly ID = BrowserViewCommandId.AddElementToChat;
|
||||
|
||||
constructor() {
|
||||
const enabled = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.equals('config.chat.sendElementsToChat.enabled', true));
|
||||
@@ -288,7 +288,7 @@ class AddElementToChatAction extends Action2 {
|
||||
}
|
||||
|
||||
class AddConsoleLogsToChatAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.addConsoleLogsToChat';
|
||||
static readonly ID = BrowserViewCommandId.AddConsoleLogsToChat;
|
||||
|
||||
constructor() {
|
||||
const enabled = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.equals('config.chat.sendElementsToChat.enabled', true));
|
||||
@@ -316,7 +316,7 @@ class AddConsoleLogsToChatAction extends Action2 {
|
||||
}
|
||||
|
||||
class ToggleDevToolsAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.toggleDevTools';
|
||||
static readonly ID = BrowserViewCommandId.ToggleDevTools;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -347,7 +347,7 @@ class ToggleDevToolsAction extends Action2 {
|
||||
}
|
||||
|
||||
class OpenInExternalBrowserAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.openExternal';
|
||||
static readonly ID = BrowserViewCommandId.OpenExternal;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -383,7 +383,7 @@ class OpenInExternalBrowserAction extends Action2 {
|
||||
}
|
||||
|
||||
class ClearGlobalBrowserStorageAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.clearGlobalStorage';
|
||||
static readonly ID = BrowserViewCommandId.ClearGlobalStorage;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -408,7 +408,7 @@ class ClearGlobalBrowserStorageAction extends Action2 {
|
||||
}
|
||||
|
||||
class ClearWorkspaceBrowserStorageAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.clearWorkspaceStorage';
|
||||
static readonly ID = BrowserViewCommandId.ClearWorkspaceStorage;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -433,7 +433,7 @@ class ClearWorkspaceBrowserStorageAction extends Action2 {
|
||||
}
|
||||
|
||||
class ClearEphemeralBrowserStorageAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.clearEphemeralStorage';
|
||||
static readonly ID = BrowserViewCommandId.ClearEphemeralStorage;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -460,7 +460,7 @@ class ClearEphemeralBrowserStorageAction extends Action2 {
|
||||
}
|
||||
|
||||
class OpenBrowserSettingsAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.openSettings';
|
||||
static readonly ID = BrowserViewCommandId.OpenSettings;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -486,7 +486,7 @@ class OpenBrowserSettingsAction extends Action2 {
|
||||
// Find actions
|
||||
|
||||
class ShowBrowserFindAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.showFind';
|
||||
static readonly ID = BrowserViewCommandId.ShowFind;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -515,7 +515,7 @@ class ShowBrowserFindAction extends Action2 {
|
||||
}
|
||||
|
||||
class HideBrowserFindAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.hideFind';
|
||||
static readonly ID = BrowserViewCommandId.HideFind;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -540,7 +540,7 @@ class HideBrowserFindAction extends Action2 {
|
||||
}
|
||||
|
||||
class BrowserFindNextAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.findNext';
|
||||
static readonly ID = BrowserViewCommandId.FindNext;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
@@ -571,7 +571,7 @@ class BrowserFindNextAction extends Action2 {
|
||||
}
|
||||
|
||||
class BrowserFindPreviousAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.browser.findPrevious';
|
||||
static readonly ID = BrowserViewCommandId.FindPrevious;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
|
||||
@@ -3,15 +3,24 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IBrowserViewService, ipcBrowserViewChannelName } from '../../../../platform/browserView/common/browserView.js';
|
||||
import { BrowserViewCommandId, IBrowserViewService, ipcBrowserViewChannelName } from '../../../../platform/browserView/common/browserView.js';
|
||||
import { IBrowserViewWorkbenchService, IBrowserViewModel, BrowserViewModel } from '../common/browserView.js';
|
||||
import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js';
|
||||
import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js';
|
||||
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
|
||||
import { Event } from '../../../../base/common/event.js';
|
||||
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
|
||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||
|
||||
export class BrowserViewWorkbenchService implements IBrowserViewWorkbenchService {
|
||||
/** Command IDs whose accelerators are shown in browser view context menus. */
|
||||
const browserViewContextMenuCommands = [
|
||||
BrowserViewCommandId.GoBack,
|
||||
BrowserViewCommandId.GoForward,
|
||||
BrowserViewCommandId.Reload,
|
||||
];
|
||||
|
||||
export class BrowserViewWorkbenchService extends Disposable implements IBrowserViewWorkbenchService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _browserViewService: IBrowserViewService;
|
||||
@@ -20,10 +29,15 @@ export class BrowserViewWorkbenchService implements IBrowserViewWorkbenchService
|
||||
constructor(
|
||||
@IMainProcessService mainProcessService: IMainProcessService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService
|
||||
) {
|
||||
super();
|
||||
const channel = mainProcessService.getChannel(ipcBrowserViewChannelName);
|
||||
this._browserViewService = ProxyChannel.toService<IBrowserViewService>(channel);
|
||||
|
||||
this.sendKeybindings();
|
||||
this._register(this.keybindingService.onDidUpdateKeybindings(() => this.sendKeybindings()));
|
||||
}
|
||||
|
||||
async getOrCreateBrowserViewModel(id: string): Promise<IBrowserViewModel> {
|
||||
@@ -67,4 +81,16 @@ export class BrowserViewWorkbenchService implements IBrowserViewWorkbenchService
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private sendKeybindings(): void {
|
||||
const keybindings: { [commandId: string]: string } = Object.create(null);
|
||||
for (const commandId of browserViewContextMenuCommands) {
|
||||
const binding = this.keybindingService.lookupKeybinding(commandId);
|
||||
const accelerator = binding?.getElectronAccelerator();
|
||||
if (accelerator) {
|
||||
keybindings[commandId] = accelerator;
|
||||
}
|
||||
}
|
||||
void this._browserViewService.updateKeybindings(keybindings);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user