From 0c52fb443b1fbcc81d615f3715fc0ca322f0d269 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Oct 2023 09:23:08 +0200 Subject: [PATCH] aux window - introduce main process service and use in some cases --- src/vs/code/electron-main/app.ts | 10 +- .../electron-main/auxiliaryWindow.ts | 43 ++++++--- .../electron-main/auxiliaryWindows.ts | 20 ++++ .../auxiliaryWindowsMainService.ts | 46 ++++++++++ .../electron-main/nativeHostMainService.ts | 91 +++++++++---------- .../auxiliaryWindowService.ts | 1 - 6 files changed, 148 insertions(+), 63 deletions(-) rename src/vs/platform/{windows => auxiliaryWindow}/electron-main/auxiliaryWindow.ts (65%) create mode 100644 src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts create mode 100644 src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 642abb829c2..e595138d35e 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -118,7 +118,8 @@ import { ElectronPtyHostStarter } from 'vs/platform/terminal/electron-main/elect import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; import { NODE_REMOTE_RESOURCE_CHANNEL_NAME, NODE_REMOTE_RESOURCE_IPC_METHOD_NAME, NodeRemoteResourceResponse, NodeRemoteResourceRouter } from 'vs/platform/remote/common/electronRemoteResources'; import { Lazy } from 'vs/base/common/lazy'; -import { AuxiliaryWindow } from 'vs/platform/windows/electron-main/auxiliaryWindow'; +import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; +import { AuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService'; /** * The main VS Code application. There will only ever be one instance, @@ -132,6 +133,7 @@ export class CodeApplication extends Disposable { }; private windowsMainService: IWindowsMainService | undefined; + private auxiliaryAindowsMainService: IAuxiliaryWindowsMainService | undefined; private nativeHostMainService: INativeHostMainService | undefined; constructor( @@ -386,7 +388,7 @@ export class CodeApplication extends Disposable { // Child Window: delegate to `AuxiliaryWindow` class const isChildWindow = contents?.opener?.url.startsWith(`${Schemas.vscodeFileResource}://${VSCODE_AUTHORITY}/`); if (isChildWindow) { - this.mainInstantiationService.createInstance(AuxiliaryWindow, contents); + this.auxiliaryAindowsMainService?.registerWindow(contents); } // Block any in-page navigation @@ -406,7 +408,7 @@ export class CodeApplication extends Disposable { return { action: 'allow', - overrideBrowserWindowOptions: AuxiliaryWindow.open(this.mainInstantiationService) + overrideBrowserWindowOptions: this.auxiliaryAindowsMainService?.createWindow() }; } @@ -992,6 +994,7 @@ export class CodeApplication extends Disposable { // Windows services.set(IWindowsMainService, new SyncDescriptor(WindowsMainService, [machineId, this.userEnv], false)); + services.set(IAuxiliaryWindowsMainService, new SyncDescriptor(AuxiliaryWindowsMainService, undefined, false)); // Dialogs const dialogMainService = new DialogMainService(this.logService, this.productService); @@ -1210,6 +1213,7 @@ export class CodeApplication extends Disposable { private async openFirstWindow(accessor: ServicesAccessor, initialProtocolUrls: IInitialProtocolUrls | undefined): Promise { const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); + this.auxiliaryAindowsMainService = accessor.get(IAuxiliaryWindowsMainService); const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP; const args = this.environmentMainService.args; diff --git a/src/vs/platform/windows/electron-main/auxiliaryWindow.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts similarity index 65% rename from src/vs/platform/windows/electron-main/auxiliaryWindow.ts rename to src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts index 4a4865ff6dd..15fe94abe64 100644 --- a/src/vs/platform/windows/electron-main/auxiliaryWindow.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts @@ -3,26 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, BrowserWindowConstructorOptions, WebContents } from 'electron'; -import { FileAccess } from 'vs/base/common/network'; +import { BrowserWindow, WebContents } from 'electron'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { defaultBrowserWindowOptions } from 'vs/platform/windows/electron-main/windows'; -export class AuxiliaryWindow { +export interface IAuxiliaryWindow { - static open(instantiationService: IInstantiationService): BrowserWindowConstructorOptions { - return instantiationService.invokeFunction(defaultBrowserWindowOptions, undefined, { - webPreferences: { - preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-sandbox/preload-slim.js').fsPath - } - }); + readonly onDidClose: Event; + + readonly id: number; + readonly win: BrowserWindow | null; +} + +export class AuxiliaryWindow extends Disposable implements IAuxiliaryWindow { + + readonly id = this.contents.id; + + private readonly _onDidClose = this._register(new Emitter()); + readonly onDidClose = this._onDidClose.event; + + private _win: BrowserWindow | null = null; + get win() { + if (!this._win) { + this._win = BrowserWindow.fromWebContents(this.contents); + } + + return this._win; } constructor( private readonly contents: WebContents, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService ) { + super(); this.create(); this.registerListeners(); @@ -38,6 +52,13 @@ export class AuxiliaryWindow { private registerListeners(): void { + // Window close + this.win?.on('closed', () => { + this._onDidClose.fire(); + + this.dispose(); + }); + // Support a small set of IPC calls this.contents.ipc.on('vscode:focusAuxiliaryWindow', () => { this.withWindow(window => window.focus(), true /* restore */); diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts new file mode 100644 index 00000000000..3fdfa14ad13 --- /dev/null +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BrowserWindowConstructorOptions, WebContents } from 'electron'; +import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IAuxiliaryWindowsMainService = createDecorator('auxiliaryWindowsMainService'); + +export interface IAuxiliaryWindowsMainService { + + readonly _serviceBrand: undefined; + + createWindow(): BrowserWindowConstructorOptions; + registerWindow(webContents: WebContents): void; + + getFocusedWindow(): IAuxiliaryWindow | undefined; +} diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts new file mode 100644 index 00000000000..b9cd6fc83f7 --- /dev/null +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BrowserWindow, BrowserWindowConstructorOptions, WebContents } from 'electron'; +import { Event } from 'vs/base/common/event'; +import { FileAccess } from 'vs/base/common/network'; +import { AuxiliaryWindow, IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; +import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { defaultBrowserWindowOptions } from 'vs/platform/windows/electron-main/windows'; + +export class AuxiliaryWindowsMainService implements IAuxiliaryWindowsMainService { + + declare readonly _serviceBrand: undefined; + + private readonly windows = new Map(); + + constructor( + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { } + + createWindow(): BrowserWindowConstructorOptions { + return this.instantiationService.invokeFunction(defaultBrowserWindowOptions, undefined, { + webPreferences: { + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-sandbox/preload-slim.js').fsPath + } + }); + } + + registerWindow(webContents: WebContents): void { + const auxiliaryWindow = this.instantiationService.createInstance(AuxiliaryWindow, webContents); + this.windows.set(auxiliaryWindow.id, auxiliaryWindow); + Event.once(auxiliaryWindow.onDidClose)(() => this.windows.delete(auxiliaryWindow.id)); + } + + getFocusedWindow(): IAuxiliaryWindow | undefined { + const window = BrowserWindow.getFocusedWindow(); + if (window) { + return this.windows.get(window.id); + } + + return undefined; + } +} diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index ea1a1daf0ba..44913bbdd5c 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -41,6 +41,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { hasWSLFeatureInstalled } from 'vs/platform/remote/node/wsl'; import { WindowProfiler } from 'vs/platform/profiling/electron-main/windowProfiling'; import { IV8Profile } from 'vs/platform/profiling/common/profiling'; +import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -52,6 +53,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain constructor( @IWindowsMainService private readonly windowsMainService: IWindowsMainService, + @IAuxiliaryWindowsMainService private readonly auxiliaryWindowsMainService: IAuxiliaryWindowsMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @@ -174,17 +176,17 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async toggleFullScreen(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.toggleFullScreen(); } async handleTitleDoubleClick(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.handleTitleDoubleClick(); } async isMaximized(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win) { return window.win.isMaximized(); } @@ -193,35 +195,35 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async maximizeWindow(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win) { window.win.maximize(); } } async unmaximizeWindow(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win) { window.win.unmaximize(); } } async minimizeWindow(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win) { window.win.minimize(); } } async moveWindowTop(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win) { window.win.moveTop(); } } async updateWindowControls(windowId: number | undefined, options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window) { window.updateWindowControls(options); } @@ -232,12 +234,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain windowId = options.windowId; } - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.focus({ force: options?.force ?? false }); } async setMinimumSize(windowId: number | undefined, width: number | undefined, height: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win) { const [windowWidth, windowHeight] = window.win.getSize(); const [minWindowWidth, minWindowHeight] = window.win.getMinimumSize(); @@ -361,24 +363,21 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Dialog async showMessageBox(windowId: number | undefined, options: MessageBoxOptions): Promise { - return this.dialogMainService.showMessageBox(options, this.toBrowserWindow(windowId)); + const { focussed } = this.windowById(windowId); + + return this.dialogMainService.showMessageBox(options, focussed ?? undefined); } async showSaveDialog(windowId: number | undefined, options: SaveDialogOptions): Promise { - return this.dialogMainService.showSaveDialog(options, this.toBrowserWindow(windowId)); + const { focussed } = this.windowById(windowId); + + return this.dialogMainService.showSaveDialog(options, focussed ?? undefined); } async showOpenDialog(windowId: number | undefined, options: OpenDialogOptions): Promise { - return this.dialogMainService.showOpenDialog(options, this.toBrowserWindow(windowId)); - } + const { focussed } = this.windowById(windowId); - private toBrowserWindow(windowId: number | undefined): BrowserWindow | undefined { - const window = this.windowById(windowId); - if (window?.win) { - return window.win; - } - - return undefined; + return this.dialogMainService.showOpenDialog(options, focussed ?? undefined); } async pickFileFolderAndOpen(windowId: number | undefined, options: INativeOpenDialogOptions): Promise { @@ -430,12 +429,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async setRepresentedFilename(windowId: number | undefined, path: string): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.setRepresentedFilename(path); } async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.setDocumentEdited(edited); } @@ -643,7 +642,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async updateTouchBar(windowId: number | undefined, items: ISerializableCommandAction[][]): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.updateTouchBar(items); } @@ -653,7 +652,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Lifecycle async notifyReady(windowId: number | undefined): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); window?.setReady(); } @@ -662,7 +661,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async reload(windowId: number | undefined, options?: { disableExtensions?: boolean }): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window) { // Special case: support `transient` workspaces by preventing @@ -690,7 +689,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async closeWindowById(currentWindowId: number | undefined, targetWindowId?: number | undefined): Promise { - const window = this.windowById(targetWindowId); + const { main: window } = this.windowById(targetWindowId); if (window?.win) { return window.win.close(); } @@ -721,7 +720,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Connectivity async resolveProxy(windowId: number | undefined, url: string): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); const session = window?.win?.webContents?.session; return session?.resolveProxy(url); @@ -737,28 +736,21 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Development async openDevTools(windowId: number | undefined, options?: OpenDevToolsOptions): Promise { - const window = this.windowById(windowId); - if (window?.win) { - window.win.webContents.openDevTools(options); + const { focussed } = this.windowById(windowId); + if (focussed) { + focussed.webContents.openDevTools(options); } } async toggleDevTools(windowId: number | undefined): Promise { - const window = this.windowById(windowId); - if (window?.win) { - const contents = window.win.webContents; - contents.toggleDevTools(); - } - - for (const browserWindow of BrowserWindow.getAllWindows()) { - if (browserWindow.webContents.getURL() === 'about:blank') { - browserWindow.webContents.toggleDevTools(); - } + const { focussed } = this.windowById(windowId); + if (focussed) { + focussed.webContents.toggleDevTools(); } } async sendInputEvent(windowId: number | undefined, event: MouseInputEvent): Promise { - const window = this.windowById(windowId); + const { main: window } = this.windowById(windowId); if (window?.win && (event.type === 'mouseDown' || event.type === 'mouseUp')) { window.win.webContents.sendInputEvent(event); } @@ -769,11 +761,11 @@ export class NativeHostMainService extends Disposable implements INativeHostMain // #region Performance async profileRenderer(windowId: number | undefined, session: string, duration: number): Promise { - const win = this.windowById(windowId); - if (!win || !win.win) { + const { main: window } = this.windowById(windowId); + if (!window || !window.win) { throw new Error(); } - const profiler = new WindowProfiler(win.win, session, this.logService); + const profiler = new WindowProfiler(window.win, session, this.logService); const result = await profiler.inspect(duration); return result; } @@ -797,11 +789,14 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#endregion - private windowById(windowId: number | undefined): ICodeWindow | undefined { + private windowById(windowId: number | undefined): { main?: ICodeWindow; focussed?: BrowserWindow } { if (typeof windowId !== 'number') { - return undefined; + return {}; } - return this.windowsMainService.getWindowById(windowId); + const main = this.windowsMainService.getWindowById(windowId); + const focussed = this.windowsMainService.getFocusedWindow()?.win || this.auxiliaryWindowsMainService.getFocusedWindow()?.win || main?.win || undefined; + + return { main, focussed }; } } diff --git a/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts index 4ca75614771..33e62180807 100644 --- a/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts @@ -30,7 +30,6 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService super(layoutService); } - protected override applyCSS(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore): void { super.applyCSS(auxiliaryWindow, disposables);