From ffda633b4a3b5350e31e4cbd99fa5dd1b1a7df91 Mon Sep 17 00:00:00 2001 From: Hemanth Soni Date: Thu, 24 Sep 2026 19:17:43 -0400 Subject: [PATCH] Keep keyboard focus off hidden browser tabs A hidden desktop Browser tab takes native keyboard focus whenever its page navigates on its own (a reload timer, meta refresh, or live reload), so typing in any other thread loses the composer. #4303 returns that focus to the host for automation-controlled tabs; apply the same guard to hidden tabs. Fixes #4308 Co-Authored-By: Claude Opus 5.5 (1M context) --- apps/desktop/src/desktop-browser-view.ts | 4 +-- .../test/desktop-browser-view-manager.test.ts | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/desktop-browser-view.ts b/apps/desktop/src/desktop-browser-view.ts index 29ef081a780..7b8857a2d8b 100644 --- a/apps/desktop/src/desktop-browser-view.ts +++ b/apps/desktop/src/desktop-browser-view.ts @@ -692,7 +692,7 @@ export function createDesktopBrowserViewManager( entry.suppressNextFocusNotification = false; return; } - if (automationControlled.has(webContents)) { + if (automationControlled.has(webContents) || !entry.visible) { setTimeout(() => returnFocusToHost(hostWindow), 0); return; } @@ -1004,7 +1004,7 @@ export function createDesktopBrowserViewManager( for (const [key, entry] of entries) { if ( key.startsWith(hostPrefix) && - automationControlled.has(entry.webContents) && + (automationControlled.has(entry.webContents) || !entry.visible) && !entry.webContents.isDestroyed() && entry.webContents.isFocused() ) { diff --git a/apps/desktop/test/desktop-browser-view-manager.test.ts b/apps/desktop/test/desktop-browser-view-manager.test.ts index ff1136fed05..69fe84b735a 100644 --- a/apps/desktop/test/desktop-browser-view-manager.test.ts +++ b/apps/desktop/test/desktop-browser-view-manager.test.ts @@ -1733,6 +1733,42 @@ describe("DesktopBrowserCdpAdapter", () => { vi.useRealTimers(); } }); + + it("returns native focus to the host from a hidden tab", async () => { + vi.useFakeTimers(); + const focusHostWebContents = vi.fn(); + const manager = createDesktopBrowserViewManager({ + partition: "persist:test", + focusHostWebContents, + }); + const hostWindow = new FakeHostWindow({ + contentBounds: { width: 700, height: 450 }, + webContentsId: 92, + }); + attachBrowserTab({ + manager, + hostWindow, + tabId: "browser:a", + url: "https://example.com", + }); + manager.setVisible({ + hostWindow, + request: { tabId: "browser:a", visible: false }, + }); + const view = requireFakeView(0); + try { + view.webContents.nativelyFocused = true; + view.webContents.emitFocus(); + await vi.advanceTimersByTimeAsync(0); + expect(focusHostWebContents).toHaveBeenCalledExactlyOnceWith(92); + expect(hostWindow.webContents.sentChannels).not.toContain( + "bb-desktop:browser:focused", + ); + } finally { + manager.destroyAll(); + vi.useRealTimers(); + } + }); }); describe("DesktopBrowserViewManager", () => {